Widget 2탄

Eddy📱·2022년 7월 30일
0

SwiftUI

목록 보기
1/2
post-thumbnail

2탄에서는 어떻게 위젯을 만드는지 보여주려고 한다.

먼저 Widget를 만들기 위해서는 파일을 생성해야한다.

프로젝트 파일에서 File -> New -> Target를 눌러서 Widget를 검색하게 되면 Widget Extension이 나오게 된다.

이를 만들어주는 것이 첫 번째 단계이다.

타겟 추가 시 Include Configuration Intent 버튼을 해제해야합니다.

만약 user-configurable 프로퍼티를 위젯이 제공하게 되면 이를 선택하면 되지만, 현재 만드는 것에서 필요없으므로 해제한다.

음.. 이게 무슨 의미인지 이해가 안될 것인다. user-configurable이 무엇이고 이는 어떻게 사용하는 것일까?

user-configurable

StaticConfiguration

  • user-configurable 프로퍼티가 없는 것은 예를 들어 주식 시장 위젯에서 일반적인 마켓 정보를 보여주거나 인기있는 헤드라인들을 보여주는 새 위젯들을 말한다.

IntentCOnfiguration

  • user-configuralbe 프로퍼티가 있는 것은 SiriKit의 커스텀 intent를 사용해서 이 프로퍼티를 정의할 수 있다. 예를 들어, 날씨 위젯은 현재 도시의 우편번호가 필요하다.
    또한 내 배송을 추적해주는 위젯에서는 추적 넘버가 필요하다.

이렇게 보니, user-configurable이 있는 것은 유저의 입력과 유저의 현재 위치 등이 주어져야 한다.
반대에서는 유저 정보가 필요없이 단순히 정보만을 제공하는 위젯을 말한다.

위젯은 앱에서 여러 extensions을 포함할 수 있더라도 하나의 widget extension에서 모든 widgets 들을 넣어줄 수 있다.

만약에 위젯 중 어떤 부분에서는 위치 정보를 사용하고 다른 부분에서는 사용하지 않는다고 하면 분리된 extension에서 위치 정보를 사용할 수 있다.

그래서 시스템에 특정 extension를 분리해서 사용하는 것을 허락한다.

이렇게 widget를 만들면 기본으로 제공하는 코드들이 있다

이에 대한 분석이 필요하다!

위젯의 3가지 구성요소

위젯을 구성할 때 필요한 요소는 3가지가 있다.

  1. 위젯으로 구성할 수 있는 내용, 어떤 뷰를 사용하고 어떤 이름을 가질것인지 정의하는 configuration
  2. 시간에 따라 위젯의 뷰를 업데이트하는 과정을 제공하는 timeline provider
  3. WidgetKit을 사용한 Swift UI 로 구성된 뷰

위젯 Configuration

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

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            wwidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}

struct wwidget_Previews: PreviewProvider {
    static var previews: some View {
        wwidgetEntryView(entry: SimpleEntry(date: Date()))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}

Main이 있는 부분이 위젯에서 가장 먼저 봐야할 부분이다.
위젯을 구성하는 body를 만들어내고 있다. 일명 위젯을 만들어주는 코드다.
StaticConfiguration를 통해 widget view를 그려주고 있다.
위에서 StaticConfiguration에 대해서는 설명했으니 기억이 안나면 다시보고 오면 된다!

이곳에서 kind는 widget extension를 만들 떄의 widget 이름을 말한다. 이를 통해 unique하게 구분하고 있다.

provider는 이제 아래에서 설명할 메서드들이 있는데 이를 적용한 구조체이므로 이를 인스턴스로 생성해주고 있다. 이를 통해 시스템에 언제 무엇을 할지 결정해주는 역할을 한다.

그리고 클로저를 통해 실제 entry를 받아서 뷰에 그려주고 있다.

provider의 메서드에는 총 3가지가 있다. 각각이 어떤 역할을 하는지 알아보도록 하자

placeholder 메서드

placeholder는 보통 inputView에서 어떤 것을 입력하면 좋을지에 대한 가이드라인 제공하는 것을 사용되고 있다.

여기에서도 비슷한 느낌인데, 표현이 제대로 되지 않는 특수한 시점에 사용되고 있다.
핸드폰이 방전되거나 메모리가 너무 없는 상황에 위젯이 제대로 표현이 안될 떄를 말한다.
그런 경우에 어떤 것을 보여줄지를 알려준다.

언제 확인하냐?
아이폰 기본 주식앱에서 핸드폰을 오랫동안 껏다가 키면 플레이스홀더처럼 보일 때가 있다.

실제로 이걸 확인해보고 싶은데.. 확인하기가 어렵다ㅠㅠ

getSnapshot 메서드

홈화면 편집누르고 내 프로젝트 입력하면 나오는 화면이 있다
이게 getSnapshot이다!
먼저 바로 실행해보면 시뮬레이터에서 아래와 같은 그림을 볼 수 있다

기본적으로 이렇게 시간을 보여주는 위젯이 제공된다.

이제 getSnapshot를 보는 방법은 먼저 위젯을 몇초간 누르고 있으면 popover가 나온다

여기에서 홈화면 편집으로 누르면 편집모드가 시작되는데

왼쪽 위에 + 버튼을 누르면 위젯을 검색할 수 있다.
이곳에서 자신의 프로젝트를 검색하면 위젯이 나오는데

여러 방식의 위젯을 제공하고 있다.

여기에서 원하는 방식의 위젯을 추가할 수 있다.

getTimeline 메서드

getTimeLine method는 시스템이 어떤 특정한 시점의 시간표를 가져가겠다는 메서드다
언제가 될지는 모르겠지만 어떤 시점에 어떤 정보를 가져가겠다는 의미다.

그래서 기본제공되는 코드를 분석해보면 더 이해하기 쉽다.

let currentDate = Date()
for hourOffset in 0 ..< 5 {
    let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
    let entry = SimpleEntry(date: entryDate)
    entries.append(entry)
}

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

현재 시간을 가져온 다음, hourOffset는 1시간단위로 총 5번의 반복을 의미한다.
그런 다음 Calender의 현재 시간을 기준으로 한시간씩 더해지며 이는 총 entryDate를 의미한다.

이것을 바탕으로 model인 SimpleEntry에 적용시킨다음

entries 배열에 넣어둔다.

그리고 마지막으로 timeline를 만들고 정책을 정하여 넘겨주게된다.
시스템에서 알아서 그 시간에 맞게 뷰를 만들어 줄 것이다

정책관련된 내용은 아래에 추가 설명하겠다!

Timeline의 Reloadpolicy 정책

위젯의 provider로 부터 새로운 timeline를 요청해서 어떤 정책인지에 따라 다르게 reload해주는 것을 말한다.

atEnd
위젯의 provider로 부터 새로운 timeline를 요청해서 새로 policy를 지정하도록 하는 정책이다.
그래서 마지막 날짜가 지나면 새로운 타임라인을 요청한다.

after
지정해둔 알람이 끝나고 내가 다시 설정해서 활성화하면 그 시간 이후에 알람을 다시 울리도록 하는 것이다. 그래서 3시간동안 각 1시간동안 울린다음 만약 after에 2시간을 넣으면 2시간 후에 다시 알림을 울리라는 의미다.

never
알람 설정을 만약 3번하라고 하면 그게 끝난다면 내가 추가로 설정하기 전까지는 아무 반응없이 가만히 있으라는 정책이다.

이 3가지 정책을 그림으로 확인하면 아래와 같다.

위젯 reload 메서드

만약 app의 상태를 변하게 되면 widget의 타임라인에도 영향을 끼칠 때가 있다. 이럴떄에는 보통 WidgetKit에게 timeline를 reload하도록 해서 문제가 되는 것을 해결할 수 있다.

아래의 방식에는 특정 kind, 아까 말했던 unique한 값이겠죠? 이 특정한 것만을 reload해줄 수 있다.

WidgetCenter.shared.reloadTimelines(ofKind: "com.mygame.gamestatus")

아래를 호출하게 되면 모든 widgets를 reload하도록 요청한다.

WidgetCenter.shared.reloadAllTimelines()

이제 만약 실제로 위젯을 custom하고 싶다면 아래의 코드에 입력이 필요하다.

struct wwidgetEntryView : View {
    var entry: Provider.Entry

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

이곳의 body는 widget의 body를 의미하므로 현재에는 entry.date가 있는 것을 보니, 현재 시간을 내보내주는 것을 알 수 있다.

SwiftUI이기 떄문에 SwiftUI 기반의 코드가 필요하다.

하나의 위젯에 다른 값들을 넣고 싶다면

VStack, HStack를 활용해서 넣어줄 수 있다.

struct wwidgetEntryView : View {
    var entry: Provider.Entry

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

이러면 아래처럼 위젯이 보이게 된다!

이렇게 직접적으로 값을 넣어주는건 좋지않는 것은 아시죠?!

아까 언급했던 모델을 활용해서 넣어주면 조금 더 나은 위젯이 될것이다!

현재는 어떻게 위젯이 보이고, 작동원리에 대해 알아보는 정도로 했다!

추가 위젯에 대해 공부하려면 wwdc를 참고하면 더 많은 지식을 얻을 수 있을 것이다.

참고사이트
https://developer.apple.com/documentation/widgetkit/widgetcenter
https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date
https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension
https://developer.apple.com/documentation/WidgetKit/Keeping-a-Widget-Up-To-Date

profile
Make a better world

0개의 댓글