[iOS] - Complications and widgets: Reloaded


Embrace Swift generics

Complication timeline

image

Complications는 watchOS 플랫폼의 핵심으로, 워치 화면에 빠르게 한눈에 알아볼 수 있는 정보를 출력한다.

  • watchOS 2에서 ClockKit은 나만의 complications를 생성할 수 있게 했다.
  • watchOS 5에서 그래픽이 발전된 complication이 소개됨
  • watchOS 7에서 SwiftUI complication과 여러 complication이 소개됐다.

image

iOS 16, watchOS 9의 WidgetKit으로 두 플랫폼에서 모두 동작 가능한 위젯을 만들 수 있다. 이를 위해 WidgetFamily 타입에 새로운 위젯을 추가했다. 모두 “accessory”로 시작한다.

image

accessoryRectangular : 여러 줄의 텍스트나 작은 그래프, 차트를 보여줄 수 있다. ClockKit의 graphicRectangular와 비슷하다.

image

accessoryCircular : 작은 정보, 게이지, 진행상황을 나타내기 좋다. ClockKit의 graphicCircular와 비슷하다.

image

accessoryInline : 시간 부분 위에 올라오는 텍스트 전용 슬롯

image

accessoryCorner : watchOS에서만 사용 가능. 게이지, 텍스트가 있는 둥그런 위젯

=> watchOS와 complication-specific 기능을 보려면 “Go further with WidgetKit complication” 세션에서 확인 가능

Colors

image

세 개의 다른 렌더링 모드.

  1. full color
  2. accented
  3. vibrant

image

  • WidgetRenderingMode 타입이 이 세 개의 다른 모드를 가지고 있다.
  • widgetRenderingMode keypath를 사용해서 Environment에서 이 값에 접근할 수 있다.
  • 접근 후에 컨텐츠를 모드에 맞게 조정한다.

Full color

image

Full-color 모드에서 컨텐츠를 내가 명시한대로 나타난다. 많은 complication이 full color로 화려하게 나타난다.

Accented

image

Accented 렌더링 모드에서 뷰는 두 종류의 그룹으로 나뉘어 다르게 색이 보여진다. .widgetAcentable() 뷰 modifier를 사용해서 그룹을 어떻게 보여줄 지 시스템에 알려줄 수도 있고, 컨텐츠를 Widget Rendering Mode environment 값에 따라 바꿀 수 있다.

image

시스템은 여러 방법으로 컨텐츠의 색을 설정할 수 있다. 어떤 건 검은 배경화면이지만 watchOS 9에서는 새로운 배경 색으로 설정할 수 있다.

Vibrant

image

iOS vibrant 렌더링 모드에서는 컨텐츠가 완전이 desaturated 된 후 잠금 화면 배경에 맞게 색칠된다. 시스템은 greyscale 컨텐츠를 material appearance로 매핑한다. 이 material은 배경에 있는 컨텐츠에 맞게 적절하게 나타난다. 추가로 잠금 화면은 vibrant 렌더링 모드에 colored tint를 줄 수 있게 구성되었다.

밝은 색은 더 불투명하고 밝게 나타나고 어두운 색은 배경을 가리지 않을 정도로만 흐리고 덜 밝게 나타난다.

image

사용자가 글자를 볼 수 있게 이 모드에서 투명한 색을 사용하기 보다는 어두운 색을 사용해서 컨텐츠를 보여주는 것이 좋다.

image

AccessoryWidgetBackground 뷰를 사용해서 위젯에 배경을 줄 수 있다.

image

배경 뷰는 위젯 렌더링 모드에 따라 다르게 나타날 수 있고 워치 화면이나 잠금 화면의 스타일에 맞게 조정될 수 있다.

Project setup

image

프로젝트에 Widget extension target을 추가해서 시작할 수 있다.

Xcode

먼저 이미 존재하는 iOS target과 코드를 공유하는 새로운 watchOS target을 추가할 것이다.

image

  1. iOS widget extension target을 복제해서 적절한 이름으로 바꿔준다.

image

  1. bundler identifier를 watch app에 맞게 바꿔준다

image

  1. watchOS로 target을 설정한다

image

  1. watch app에 새로운 extension을 embed한다.

Code 설명

이미 프로젝트에 존재하는 코드를 간략하게 설명하고 넘어가는 부분이므로 자세히 안 봐도 된다

image

  • TimelineProvidier : 시스템이 컨텐츠를 reload할 때 사용됨

image

  • SwiftUI가 다른 familiy들에 맞게 컨텐츠를 생성할 때 사용하는 뷰

image

  • widget configuration

image

  • Xcode preview provider

이 앱은 이미 iOS 홈 스크린 위젯을 지원하는데, 새로운 family를 추가할 것이다.

image

System family는 watch에서 쓸 수 없기 때문에 supportedFamilies를 플랫폼에 따라 다르게 하기 위해 platform macro를 사용했다.

image

그리고 preview provider에 새로운 family를 위한 preview를 추가했다.

image

이제 watchOS에 빌드하기 전에 새로운 IntentRecommendation API를 구현해야 한다. Intent가 configurable하기 때문에 preconfigured list를 제공해야 한다. 이를 IntentTimelineProvider의 새로운 recommendations 메서드를 오버라이딩 해서 할 수 있다.

image

이제 빌드가 잘 된다.

image

작은 위젯을 위한 컨텐츠도 새로운 form factor에 잘 맞지 않는 것을 확인할 수 있다. 새로운 위젯 family는 홈 화면의 iOS 위젯보다 더 작기 때문에 complication의 컨텐츠를 신경써야 한다.

Making glanceable views

Circular

image

  • progressViewStyle 를 추가해서 진행상황의 정도를 나타낼 수 있다.

image

이 prgress view에 애니메이션을 적용하는게 많은 timeline entry를 필요로 할 수 있다는 점이다. 대신, SiwftUI의 새로운 auto-updating ProgressView를 사용할 수 있다. 오직 하나의 timeline entry만 필요하게 된다.

Rectangular

image

Rectangular preview는 더 많은 공간을 차지하기 때문에 여기에서는 3줄이 들어가게 만들었다. 캐릭터의 이름을 강조하기 위해 텍스트 사이즈를 설정하고, headline 폰트 스타일을 사용하고, widgetAccentable modifier를 사용해서 색을 설정한다.

image

Accent mode에서는 위와 같이 보인다. 캐릭터의 이름이 accent 색을 띄고 있다. 환경에 맞게 위젯과 complication이 어우러지게 하려면 기본 font 파라미터와 스타일을 사용하는 것이 중요하다.

iOS와 watchOS의 폰트 스타일과 크기는 다르다. iOS는 일반적인 텍스트 디자인을 사용하는데, watchOS는 더 두꺼우면서 둥그런 텍스트 디자인을 사용한다.

image

위젯과 complication은 iOS와 watchOS에서 인접하게 나타난다. 폰트 스타일을 Title, Headline, Body, Caption을 사용하는 것을 권장한다.

imageimage

watch OS에서 아바타를 추가로 띄웠는데, iOS에서 확인해도 똑같이 잘 나온다.

accessoryInline

image

accessoryInline 스타일은 텍스트와 이미지를 표시할 수 있다. Inline accesory는 시스템에서 정의된 색과 폰트가 지정된다는 것에 주의하자.

워치 화면 하단에 텍스트를 출력하게 했는데, 공간에 비해 너무 길다. 이 경우 ViewThatFits를 사용한다.

image

길이가 다양한 여러 뷰를 제공할 수 있는데, 이러면 ViewThatFits는 잘리는 일 없이 사용가능한 공간에 딱 맞는 첫 번째 뷰를 선택할 것이다. 그래서 여기에서는 두 번째 Text가 워치 화면에서 보여지고 있다.

image

만약 저 두 줄의 주석문이 주석 해제가 된다면 두 번째 Text가 출력될 것이다.

Privacy

디바이스가 어떤 상태에 있는지를 고려해야 한다.

  • redacting content state : 보안상 컨텐츠를 보이지 않게 하는 모드
  • low-luminance state : 화면 밝기가 낮은 상태

image

iOS 잠금 화면의 경우 기본적으로는 디바기으가 잠겨져 있어도 컨텐츠를 보이게 한다.

image

하지만 Settings에서 이런 설정을 바꿔서 사용자가 기기가 잠겼을 때 컨텐츠를 보이지 않게 설정할 수 있다.

image

watch OS에서 기기는 사람이 차고 있는 한 잠금 해제 상태다. 차고 있지만 사용하고 있지 않은 상태에서는 always-on 상태가 되고, 컨텐츠는 밝기를 낮춰 보여준다.

기본적으로 low luminance 상태에서 컨텐츠는 감춰지지 않는다.

image

잠금화면과 마찬가지로 사용자가 이 always-on 상태에서 complication content를 감추게 설정할 수 있다. 이 상태에서는 컨텐츠가 모두 redaction과 Low luminance 모드 모두에서 잘 작동되도록 보장해야 한다.

image

워치에서 위젯은 always-on display 를 지원해야 한다. 이를 \.isLuminanceReduced environment 값을 통해 always-on 모드에서 컨텐츠가 어떻게 나타나게 할 지 설정할 수 있다.

image

기본적으로 안전 모드는 TimelineProvider가 생성하는 뷰에서 컨텐츠를 감춘 뷰를 보여줄 것이다. .privacySensitive modifier를 사용해서 감춰질 뷰만을 표시할 수 있다.