[swift]Widget 둘러보고 간단히 만들어보기(1)

okstring·2021년 12월 24일
0

Widget

목록 보기
1/2
post-thumbnail

위젯을 통해 사용자와 인터렉션을 증가시켜봅시다!
무작정 만들어보기 전에 알아보는 포스팅 먼저 진행합니다

WidgetKit이란

SwiftUI용 위젯 API 및 WidgetKit 프레임워크를 사용하여 손쉽게 위젯을 빌드하고 iOS, iPadOS 및 macOS 어디에서나 사용 가능하게 할 수 있습니다(생략)

H.I.G에서는 다음과 같은 내용이 언급됩니다

  • iOS 및 iPadOS에서 위젯 갤러리는 Smart Stack을 포함한 위젯 스택도 지원

    사람들이 관심을 가질 가능성이 있을 때 Siri는 제안된 위젯을 Smart Stack에 추가할 수 있습니다.

  • 앱의 주요 목적과 명확하게 관련된 간단한 아이디어를 찾으세요.

  • 위젯의 각 크기에는 위젯의 아이디어와 직접적으로 관련된 정보만 표시하도록 합니다.

  • 앱을 실행하는 것 외에는 아무것도 하지 않는 위젯을 만들지 마십시오.

  • 하루 종일 바뀌는 동적 정보를 선호합니다.

  • 위젯을 최신 상태로 유지하십시오.

  • 너무 많은 상호작용 대상을 정의하지 마세요

    (뭔가 될 것 같았지만 우리가 만드는 widget은 스크롤이나 Smart Stack처럼 페이징이 안된다)

Widget 둘러보기

무작정 일단 만들러 가봅니다!

File - New - Target을 선택하고 WidgetExtension을 클릭하면 Widget Extension을 추가할 수 있습니다

클릭을하면 여기서 이런 창을 보실 수 있습니다. 저기서 체크박스인 Include Configuration Intent가 있는데 여기서 위젯의 종류가 구분됩니다.

StaticConfiguration

For a widget with no user-configurable properties. For example, a stock market widget that shows general market information, or a news widget that shows trending headlines.

사용자 구성 가능한 속성이 없는 위젯의 경우. 예를 들어, 일반적인 시장 정보를 표시하는 주식 시장 위젯이나 트렌드 헤드라인을 표시하는 뉴스 위젯이 있습니다.

IntentConfiguration

For a widget with user-configurable properties. Use a SiriKit custom intent to define the properties. For example, a weather widget that needs a zip or postal code for a city, or a package-tracking widget that needs a tracking number.

즉! StaticConfiguration과 달리 사용자가 구성할수 있는 프로퍼티가 있는, 위젯 편집을 제공하는 방식입니다

위와 같이 스크린타임 위젯이 IntentConfiguration 해당합니다

구성요소 살펴보기

저는 일단 StaticConfiguration로 해보고 확인버튼을 누르면 생성이 됩니다. 뭔가 코드가 굉장히 많은데 크게 나눠서 하나씩 살펴보도록 하겠습니다

Kind

위젯을 식별하는 문자열입니다. 애플 문서에서는 "com.mygame.game-status" 처럼 작성했네요

provider

TimelineProvider를 준수하고 위젯을 렌더링할 때 WidgetKit에 알려주는 타임라인을 생성하는 개체입니다. 여기에는 사용자가 정의한 TimelineEntry type이 포함됩니다.

Content Closure

WidgetKit은 Content Closure 호출하여 위젯의 콘텐츠를 렌더링하고 공급자로부터 TimelineEntry 매개변수를 전달합니다.

우리가 방금 만들었던 곳에서는

func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ())

func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ())

의 클로저가 해당되겠네요. 그리고 사용자가 정의해놓은 Entity를 전달한다고 하니

TimelineEntry

struct SimpleEntry: TimelineEntry {
    let date: Date
}

처럼 Entity를 만들 수 있습니다.

struct PhotoWidgetEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        Text(entry.date, style: .time)
    }
}

그리고 여기서는 넘어온 것들로 View를 구성할 수 있는겁니다. Snapshot과 함께요!

그리고 위젯을 선택할 때 모습을 다들 알고 계실꺼에요 여기를 위젯 갤러리라고 하고 context.isPreviewtrue 일 때 여기에 표시되는데 그 여정을 한번 더 살펴보면

  1. widget’s provider는 위젯에 대한 타임라인을 생성하고 각 항목에 세부정보를 포함합니다.
  2. 각 timeline 항목의 날짜가 도착하면 WidgetKit은 위젯의 콘텐츠를 표시하기 위해 content closure를 호출합니다.
  3. 마지막으로 수정자는 위젯 갤러리에 표시되는 이름과 설명을 지정하고 사용자가 위젯의 소형, 중형 또는 대형 버전을 선택할 수 있도록 합니다.

여기서 3번의 경우가 이 코드가 되겠습니다. widgetFamily 라고 하는데 크기에따라 View 구성을 다르게 해야겠죠!?

@main
struct PhotoWidget: Widget {
    let kind: String = "PhotoWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            PhotoWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget") // 이름
        .description("This is an example widget.") // 설명
        .supportedFamilies([.systemSmall, .systemMedium]) // 지원하는 위젯 크기
    }
}

참고로 앱을 한번 이상 실행해야 위젯갤러리에 우리의 위젯이 보입니다

그리고 2번의 timeline을 좀 더 깊게 살펴보겠습니다

TimeLineProvider

다양한 시간에 WidgetKit은 공급자에게 타임라인을 요청합니다. 그리고 WidgetKit은 다음 두 가지 방법 중 하나로 타임라인 항목을 요청합니다.

위젯의 현재 상태를 나타내는 단일 즉각적인 스냅샷

이 경우에는 위젯 갤러리에 스냅샷을 보여주는것을 뜻합니다. 스냅샷은 가져오는데 시간이 걸릴 경우 사용자 경험에 안좋으므로 위에서 봤던 context.isPreview 를 통해 샘플데이터를 보여주는것을 추천합니다

현재 순간과 위젯의 상태가 변경될 미래 날짜(알려져 있는 경우)를 포함하는 항목의 배열입니다.

위젯을 추가하고나서 타임라인을 요청하게 되는데 위젯을 업데이트 위해 활성화할 때를 알아야 합니다. Provider가 생성하는 타임라인은 위젯 업데이트를 원할 때 WidgetKit에 알립니다. 예를들어 가상의 앱 안의 캐릭터 체력이 25%일때 timeline 생성한다면 아래와 같은 코드가 완성됩니다

struct CharacterDetailProvider: TimelineProvider {
    func getTimeline(in context: Context, completion: @escaping (Timeline<CharacterDetailEntry>) -> Void) {
        var date = Date()
        var healthLevel = 0.25
        var entries: [CharacterDetailEntry] = []

        while healthLevel <= 1 {
            // Add the current health level for a given date.
            entries.append(CharacterDetailEntry(date: date, healthLevel: healthLevel))

            // Health recovers at 25 percent per hour, with a maximum of 100 percent.
            healthLevel = min(1, healthLevel + 0.25)

            // Move the date forward by 1 hour.
            date = Calendar.current.date(byAdding: .hour, value: 1, to: date)!
        }

        // Create the timeline and call the completion handler.
        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

새로고침 정책

let timeline = Timeline(entries: entries, policy: .atEnd)

타임라인을 생성할 때 Provider는 Widgetkit이 새 타임라인을 요청할 때 제어하는 새로고침 정책을 지정합니다. 기본은 .atEnd를 사용하여 타임라인의 항목으로 지정된 마지막 알짜 이후 새 타임라인을 요청하는 것이지만 새 타임라인을 요청해야하는 다른 날짜가 있는 경우 .after(date:)의 새로고침 정책을 지정할 수 있습니다

또한 위젯을 Refresh하는데에는 제한이 있습니다. 물론 Xcode에서 위젯을 디버깅할 때는 refresh가 계속해서 되지만 제한이 되는 경우를 유의깊게 알아봐야 합니다.

다음 포스팅에는 기존에 다른 타겟에서 가지고 있는 URL로 네트워크 요청을 해 프로필 이미지를 받아와 위젯에 표시하는 간단한 예제를 만들어 보겠습니다


reference

https://eunjin3786.tistory.com/212

https://zeddios.tistory.com/1088?category=796110

https://avatars.githubusercontent.com/u/62657991?v=4

https://developer.apple.com/documentation/widgetkit/timelineprovider
https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension

profile
step by step

0개의 댓글