스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
새 위치로 위젯 가져오기
위젯 생태계가 확장되고 있습니다. 최신 WidgetKit API를 사용해 위젯이 어디서든 멋지게 보이도록 설정하는 방법을 알아보세요. 위젯의 백그라운드를 확인하고 레이아웃을 동적으로 조정하며 바이브런트 렌더링을 위해 색상을 준비해서 어떤 환경에서든지 위젯을 원활하게 배치하는 방법을 보여 드립니다.
챕터
- 0:00 - Introduction
- 1:44 - Transition to content margins
- 3:00 - Add a removable background
- 4:31 - Dynamically adjust layout
- 6:00 - Prepare for vibrant rendering
리소스
관련 비디오
WWDC23
WWDC22
-
다운로드
♪ ♪
안녕하세요, 캐스린입니다 시스템 경험 엔지니어예요 이번 시간에는 위젯의 새로운 위치와 어디서든 근사하게 보이도록 위젯을 쉽게 최적화하는 방법을 알아보죠 위젯의 역사부터 간략히 살펴볼까요? iOS 14는 홈 화면에 위젯을 도입했고 iOS 16은 잠금 화면으로 위젯을 가져왔습니다 간편한 WidgetKit API를 똑같이 사용해서요 올해 위젯 생태계는 새 위치 네 곳으로 확장됩니다 바로 Mac 데스크톱과 iPad 잠금 화면 새로운 iPhone 스탠바이 모드 새로운 Apple Watch 스마트 스택인데요 사람들이 훨씬 더 많은 위치에서 선호하는 위젯을 모두 볼 수 있다는 뜻이죠 위젯은 모든 위치에서 자동으로 나타날 겁니다 따라서 어디서 표시되든 근사하게 보여야겠죠 Mac의 iPhone 위젯 덕분에 현재 여러분에게 macOS 앱이 없더라도 여러분의 Mac 위젯을 사람들이 사용할 수 있겠죠 제 친구 데번과 그레이엄이 새로운 위치에서 사용할 수 있도록 Emoji Rangers 위젯 업데이트를 도와 달라고 부탁하더군요 관련 프로젝트는 WWDC20의 '위젯 코드 만들기'와 WWDC22의 '문제와 위젯: 새로 고침'에서 다뤘던 바 있는데요 우선 위젯의 새로운 콘텐츠 여백과 Apple Watch에서 전환하는 방법을 살펴본 뒤 자동 제거 가능 백그라운드를 위젯에 추가해 보겠습니다 그런 다음 환경에 맞게 레이아웃을 동적으로 확장할 수 있도록 위젯을 수정할 겁니다 마지막으로 위젯의 요소가 바이브런트 렌더링을 위해 준비됐는지 확인할게요 이제 시작해 봅시다 올해 위젯에서 새로운 점은 콘텐츠 여백입니다 콘텐츠 여백은 위젯 본문에 자동으로 적용되는 패딩으로 위젯 컨테이너의 가장자리에 콘텐츠가 접근하지 못하게 하죠 여백의 두께는 위젯이 보이는 환경에 따라 더 크거나 작을 수 있어요 watchOS 9 이하에서는 위젯이 시스템 정의 안전 영역을 사용하여 콘텐츠가 가장자리에 너무 가까이 가지 않도록 합니다 ignoresSafeArea 같은 수정자를 뷰에 추가해서 안전 영역 외부로 확장하도록 허용할 수도 있어요 watchOS 10 이상에서는 위젯의 안전 영역이 콘텐츠 여백 사용으로 대체되었죠 그러므로 이제 ignoresSafeArea 같은 수정자는 위젯에 영향을 주지 못합니다 위젯 대부분은 이런 변경 사항이 생겨도 모습엔 변함이 없습니다 안전 영역을 무시하는 콘텐츠가 위젯에 있더라도 동일한 효과를 얻을 수 있어요 contentMarginsDisabled 수정자를 위젯 구성에 추가하면 됩니다 기본 콘텐츠 여백 내에 위치해야 하는 콘텐츠의 경우 다시 padding을 추가하면 되고요 widgetContentMargins 환경 변수를 사용해서 현재 환경에 기본 여백을 가져올 수 있습니다 새로운 수정자 및 변수와 함께 콘텐츠 여백은 위젯이 보이는 플랫폼이라면 어디서든 사용 가능합니다 이제 제거 가능 백그라운드를 Emoji Rangers 위젯에 추가합시다 기존 위젯 액세서리 제품군은 iPad 잠금 화면에서 자동으로 작동합니다 iPhone에서처럼요 또한 iPad는 시스템 소형 위젯을 바로 옆에 표시할 수 있어요 지금 잠금 화면에서는 이렇게 보입니다 다른 위젯과 더 잘 어울리도록 이 환경에서 백그라운드를 제거해야 합니다 다행히도 방법은 아주 간단해요 시스템 소형 위젯의 뷰를 작성한 코드입니다 지금은 ZStack을 사용하여 gameBackground 색 위에 계층화된 AvatarView로 구성돼 있죠 백그라운드 색을 제거할 수 있도록 하려면 containerBackground 수정자를 뷰에 추가하면 됩니다 gameBackground 색은 내부로 옮기고요 이렇게 하면 표시되는 위치에 따라 위젯의 백그라운드를 시스템이 자동으로 제거할 수 있죠 Apple Watch의 스마트 스택은 또한 이 새로운 containerBackground를 활용할 수 있습니다 기본적으로 accessoryRectangular 위젯은 다크 머티리얼 백그라운드를 이 환경에서 받습니다 뷰에 동일한 수정자 containerBackground를 추가하면 해당 위젯이 다른 위젯과 동일한 백그라운드에 배치되고 앱의 시각적 정체성에 연결됩니다 새로운 환경을 더 자세히 알아보고 싶다면 'Apple Watch 스마트 스택을 위한 위젯 설계하기'를 참고해 주세요 사진 및 지도 위젯 같은 일부 위젯에는 뚜렷한 포그라운드 콘텐츠가 없기 때문에 제거할 백그라운드가 딱히 없습니다 이런 경우 수정자 containerBackgroundRemovable을 WidgetConfiguration에 추가하고 false로 설정합니다 이제 iPad 잠금 화면과 스탠바이 모드에서 사용할 수 있도록 레이아웃을 최적화해 보죠 날씨 위젯은 위젯 백그라운드가 제거됐을 때 레이아웃 변경 방법을 보여 주는 가장 좋은 예입니다
위젯이 동일한 정보를 포함하지만 공간을 효율적으로 활용하고자 최적화된 모습인데요 콘텐츠는 가장자리로 밀리고 중요한 요소가 확장됐습니다 이런 변경 덕분에 멀리서도 위젯을 읽기 쉽고 스탠바이 모드로 원활하게 통합했죠 이런 레이아웃 변화는 위젯이 iPad 잠금 화면에서 액세서리 제품군 위젯과 조화를 이루는 데 도움이 되고 모든 제품군에서 일관된 모습을 유지할 수 있죠 이제 Xcode로 넘어가 자체 위젯에서 이런 변화를 구현해 봅시다 시스템 소형 위젯에 사용하는 AvatarView의 코드입니다 오른편에는 Xcode Previews에서 위젯이 어떤 모습인지 보여 주는 라이브 프리뷰가 있습니다 컨테이너 백그라운드가 제거된 콘텍스트에서 표시될 때 콘텐츠가 가장자리까지 표시되도록 위젯 콘텐츠 여백은 자동으로 축소됩니다 showsWidgetContainerBackground 환경 변수를 사용하여 위젯 백그라운드 제거 여부를 감지할 수 있어요 이때 히어로 레벨과 경험치 세부 정보를 HeroNameView에서 생략하고 대신 아래에 표시합니다
또한 이런 환경에서 히어로 이름이 더 커질 겁니다 이 콘텍스트 사이에서 전환하면 레이아웃이 자동으로 바뀝니다 원래 뷰에서 새롭게 확장된 뷰로요
액세서리 제품군 위젯과 마찬가지로 시스템 제품군 위젯은 바이브런트 렌더링 모드로 iPad 잠금 화면에 표시됩니다 위젯의 채도가 감소해서 잠금 화면 백그라운드로 적합하게 색이 조정되는 거예요 이렇게 렌더링하면 대비로 인한 문제가 위젯의 판독성에 영향을 줄 수 있습니다 여기는 히어로의 아바타를 원형 배경에서 식별하기 힘든 상태로군요 Xcode로 되돌아가서 위젯을 수정해 바이브런트 렌더링 모드일 때 배경을 제거해 볼게요 스탠바이 야간 모드에서 확인해 봅시다 widgetRenderingMode 환경 변수를 사용해 현재 렌더링 모드를 감지합니다 아바타의 includesBackground 매개변수를 바꿔서 바이브런트 렌더링 모드가 맞는지 확인해 볼게요
스탠바이 야간 모드 또한 바이브런트 렌더링을 사용하므로 이 콘텍스트에서도 아바타가 명확히 잘 보입니다 위젯 렌더링 모드를 더 자세히 알고 싶다면 WWDC22에서 '문제와 위젯: 새로 고침'을 참고해 보세요 위젯의 새 기능이 궁금하다면 '위젯 활성화하기'를 참고해 보시고요 위젯에 적용될 새 변경 사항이 정말 기대됩니다 여러분이 새로운 기능을 활용해 어떻게 위젯을 향상할지도 아주 기대돼요 감사합니다, 반가웠어요 ♪ ♪
-
-
2:08 - SafeAreasWidget
struct SafeAreasWidgetView: View { @Environment(\.widgetContentMargins) var margins var body: some View { ZStack { Color.blue Group { Color.lightBlue Text("Hello, world!") } .padding(margins) } } } struct SafeAreasWidget: Widget { var body: some WidgetConfiguration { StaticConfiguration(...) {_ in SafeAreasWidgetView() } .contentMarginsDisabled() } }
-
3:19 - EmojiRangerWidget systemSmall
struct EmojiRangerWidgetEntryView: View { var entry: Provider.Entry @Environment(\.widgetFamily) var family var body: some View { switch family { case .systemSmall: ZStack { AvatarView(entry.hero) .widgetURL(entry.hero.url) .foregroundColor(.white) } .containerBackground(for: .widget) { Color.gameBackground } } // additional cases } }
-
3:48 - EmojiRangerWidget accessoryRectangular
var body: some View { switch family { case .accessoryRectangular: HStack(alignment: .center, spacing: 0) { VStack(alignment: .leading) { Text(entry.hero.name) .font(.headline) .widgetAccentable() Text("Level \(entry.hero.level)") Text(entry.hero.fullHealthDate, style: .timer) }.frame(maxWidth: .infinity, alignment: .leading) Avatar(hero: entry.hero, includeBackground: false) } .containerBackground(for: .widget) { Color.gameBackground } // additional cases }
-
4:22 - PhotoWidget
struct PhotoWidget: Widget { public var body: some WidgetConfiguration { StaticConfiguration(...) { entry in PhotoWidgetView(entry: entry) } .containerBackgroundRemovable(false) } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.