이 때 Widget 프로토콜을 채택한 구조체의 body 프로퍼티는 위젯의 콘텐츠가 사용자가 정의할 수 있냐 없냐를 결정한다.
StaticConfiguration: User-configurable property가 없는 위젯을 위한 configuration
IntentConfiguration: User-configurable property가 있는 위젯을 위한 configuration
이것은 Widget Extension을 생성할 때, 체크박스를 통해 처음에 정해줄 수 있다.
properties
kind
widget의 identifier
provider
TimelineProvider 프로토콜을 채택하는 객체
WidgetKit에게 위젯을 언제 업데이트할 지 timeline을 제공
timeline은 TimelineEntry 타입을 가지고 있고 이것이 위젯을 업데이트할 시간, Date타입을 가지고 있다.
timeline은 하나 이상의 timelineEntry로 이루어져있고 후속 timeline을 요청할 정책으로 이루어져있다.
// 1개 entry와 15분 후, 새로운 요청을 주는 리로드 정책과 함께 Timeline을 생성하는 예제
struct GameStatusProvider: TimelineProvider {
func getTimeline(in context: Context, completion: @escaping (Timeline<GameStatusEntry>) -> Void) {
// Create a timeline entry for "now."
let date = Date()
let entry = GameStatusEntry(
date: date,
gameStatus: gameStatusFromServer
)
// Create a date that's 15 minutes in the future.
let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: date)!
// Create the timeline with the entry and a reload policy with the date
// for the next update.
let timeline = Timeline(
entries:[entry],
policy: .after(nextUpdateDate)
)
// Call the completion to pass the timeline to WidgetKit.
completion(timeline)
}
}
widgetFamily를 이용하여 위젯의 사이즈와, 잠긴 화면에 어떤 위젯을 보여줄 지 나눠 줄 수 있다.
struct GameStatusView : View {
@Environment(\.widgetFamily) var family: WidgetFamily
var gameStatus: GameStatus
var selectedCharacter: CharacterDetail
@ViewBuilder
var body: some View {
switch family {
case .systemSmall: GameTurnSummary(gameStatus)
case .systemMedium: GameStatusWithLastTurnResult(gameStatus)
case .systemLarge: GameStatusWithStatistics(gameStatus)
case .systemExtraLarge: GameStatusWithStatisticsExtraLarge(gameStatus)
case .accessoryCircular: HealthLevelCircular(selectedCharacter)
case .accessoryRectangular: HealthLevelRectangular(selectedCharacter)
case .accessoryInline: HealthLevelInline(selectedCharacter)
default: GameDetailsNotAvailable()
}
}
}
Widgetkit
시작하기
Include 뭐시기
체크박스는 아래에서 설명해줌.Configuration Deitals 추가
StaticConfiguration
: User-configurable property가 없는 위젯을 위한 configurationIntentConfiguration
: User-configurable property가 있는 위젯을 위한 configurationproperties
kind
provider
TimelineProvider
프로토콜을 채택하는 객체TimelineEntry
타입을 가지고 있고 이것이 위젯을 업데이트할 시간, Date타입을 가지고 있다.intent
content
modifiers
추가적인 configuration 정보를 modifier을 통해 제공 가능하다.
@main
어노테이션로 마크한 구조체는 한 개의 위젯을 제공할 때, 위젯 익스텐션의 엔트리 포인트이다.Declare Multiple Widgets in Your App Extension
을 보시오!Provide Timeline Entries
작동 방법에 대해 설명
func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) { let date = Date() let entry: GameStatusEntry
}
timeline은 하나 이상의 timelineEntry로 이루어져있고 후속 timeline을 요청할 정책으로 이루어져있다.
Placeholder 표시 + 민감한 데이터 숨기기
placeholder view는 콘텐츠가 없을 때, 보여주는 일반적인 뷰
예를 들어, 백그라운드에서 데이터를 로드하는 동안, redacted(reason:) modifier를 이용해서 placeholder를 생성한다.
이 modifier는 자동으로 위젯의 뷰를 자동으로 렌더링한다.
데이터가 로드가 완료되고, 뷰에 띄어주기 전, 민감한 정보를 숨기기 위하여 redacted(reason:) 콜백의 [unredacted()](https://developer.apple.com/documentation/SwiftUI/View/unredacted()) modifier를 사용해라.
처음 보여지는 경우 외에도, Data Protection을 위하여 WidgetKit은 placeholder를 사용한다.
아래와 같은 경우
콘텐츠 보여주기
widgetFamily를 이용하여 위젯의 사이즈와, 잠긴 화면에 어떤 위젯을 보여줄 지 나눠 줄 수 있다.
Dynamic 콘텐츠??
User Interaction에 대한 respond
여러개의 위젯을 사용할 경우
Preview Widget
Group { EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), relevance: nil, character: .panda)) .previewContext(WidgetPreviewContext(family: .accessoryCircular)) .previewDisplayName("(family)") EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), relevance: nil, character: .panda)) .previewContext(WidgetPreviewContext(family: .accessoryRectangular)) .previewDisplayName("(family)") EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), relevance: nil, character: .panda)) .previewContext(WidgetPreviewContext(family: .accessoryInline)) .previewDisplayName("(family)") EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), relevance: nil, character: .panda)) .previewContext(WidgetPreviewContext(family: .systemSmall)) .previewDisplayName("(family)") EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), relevance: nil, character: .panda)) .previewContext(WidgetPreviewContext(family: .systemMedium)) .previewDisplayName("(family)") }