Complication timeline
new API
새로운 WidgetFamily - accessory 접두사가 붙음
public enum WidgetFamily {
...
case accessoryCircular
case accessoryRectangular
case accessoryInline
// watchOS 전용
case accessoryCorner
}
accessoryRectangular: 여러 줄의 텍스트와 그래픽, 차트 등을 보여주기 위함. ClockKit의 graphicRectangular와 해당
accessoryCircular: 간단한 정보, 게이지, 프로그레스 등을 보여주는데 적합. ClockKit의 graphicCircular에 해당
accessoryInline: 텍스트만 들어갈 수 있는 영역. WatchOS에서는 여러군데에, iOS에서는 시간 위에 배치 가능. 기존에는 없던 family
accessoryCorner: 작은 원형 위젯 + 게이지 및 텍스트. 이번 세션에서는 공통된 family만 다루기 때문에 해당 패밀리는 다른 세션(Go Further with WidgetKit complications)에서 다룸
Colors
제공되는 렌더링 모드
public struct WidgetRenderingMode {
static var fullColor: WidgetRenderingMode
static var accented: WidgetRenderingMode
static var vibrant: WidgetRenderingMode
}
struct MyWidgetView: View {
@Environment(\\.widgetRenderingMode) var renderingMode
var body: some View {
switch renderingMode {
// ...
}
}
}
Full Color(WatchOS): 명시된 대로 보임
accented(WatchOS): 뷰가 두개의 컬러 그룹으로 나뉘고, 이 그룹별로 플랫하게 색이 입혀짐. 원래 뷰의 opacity만 유지된다.
widgetAccentable() modifier로 그룹을 지정하거나, 렌더링 모드에 따라서 분기 시키는 방식으로 구분한다.
VStack(alignment: .leading) {
Text("Headline")
.font(.headline)
.widgetAccentable()
Text("Body 1")
Text("Body 2")
}.frame(maxWidth: .infinity, alignment: .leading)
vibrant(iOS 잠금화면): 색채가 다 빠지고, 잠금화면에 맞게 색이 지정된다.(grayscale값을 참조해서, 뒤 컨텐츠에 맞게 조정된다.)
회색조 뿐 아니라 틴트를 주도록 설정할 수도 있다. 이 때는 밝은 색일수록 더 불투명하고 명도가 높아진다.
투명한 색상을 쓰면 배경에 가려질 수 있기 때문에 덜 중요한 컨텐츠는 어두운 색상으로 표현할 것
ZStack {
AccessoryWidgetBackground() // 일관적인 위젯 배경을 위해 미리 제공하는 프리셋
VStack {
Text("MON")
Text("6")
.font(.title)
}
}
AccessoryWidgetBackground
Project setup
watchOS용 화면을 만들려면, Widget 타겟을 watchOS로 지정하면 된다.
system 접두사가 붙은 위젯은 watchOS에서 지원하지 않기 때문에 코드를 공유하는 경우 분기가 필요하다.
iOS는 Intent를 Widget 편집 UI에서 설정할 수 있지만, watchOS에서는 프리셋을 만들어줘야 한다.
func recommandations() ->
[IntentRecommendation<DynamicCharacterSelectionIntent>] {
return recommendedIntents()
.map { intent in
return IntentRecommendation(intent: intent, description: intent.hero!.displayString)
}
}
Making glanceable views
accessoryCircular예시
ProgressView(interval: entry.character.injuryDate...entry.character.fullHealthDate,
countdown: false,
label: { Text(entry.character.name) },
currentValueLabel: {
Avatar(character: entry.character, includeBackground: false)
})
.progressViewStyle(.circular)
accessoryRectangular 예시
HStack(alignment: .center, spacing: 0) {
VStack(alignment: .leading) {
Text(entry.character.name)
.font(.headline)
.widgetAccentable()
Text("Level \\(entry.character.level)")
Text(entry.character.fullHealthDate. style: .timer)
}.frame(maxWidth: .infinity, alighment: .leading)
Avatar(character: entry.character, includeBackground: false)
}
기본 폰트 스타일로 지정하는 것을 권장. 다른 위젯과 같이 놓았을 때 자연스럽게 하기 위해서.
accessoryInline 예시 - 텍스트 및 이미지(옵션)
지정된 컬러와 폰트로만 그려진다.
이 때 ViewThatFits로 하면, 사이즈에 맞는 뷰를 자동으로 선택해준다.
ViewThatFits {
Text("\\(entry.character.name) is resting, combat-ready in \\(entry.character.fullHealthDate, style: .relative)")
Text("\\(entry.character.name) ready in \\(entry.character.fullHealthDate, style: .timer)")
Text("\\(entry.character.avatar) \\(entry.character.fullHealthDate, style: .timer)")
}
privacy
low luminance: always on display 상태를 의미. 이 때는 밝기도 줄어들고 갱신 빈도도 낮아진다.
low luminance 상태 확인
@Environment(\\.isLuminanceReduced)
var isLuminanceReduced
var body: some View {
if isLuminanceReduced {
Text("🙈").font(.title)
} else {
Text("🐵").font(.title)
}
}
잠금 상태 대응
VStack(spacing: -2) {
Image(systemName: "heart")
.font(.caption.bold())
.widgetAccentable()
Text("\\(currentHeartRate)")
.font(.title)
.privacySensitive()
}