-
[ios - SwiftUI] Widget 사용하기 (2/2)ios 2021. 2. 4. 15:37
위 이미지는 뉴스 API를 이용해 받아온 데이터 리스트를 선택하면 해당 뷰로 이동할 수 있도록 제작한 위젯입니다.
이번 글에서 코드를 살펴보도록 하겠습니다.
struct myStaticWidgetEntryView : View { var entry: Provider.Entry var body: some View { VStack { Color(.systemOrange) Text("최신 뉴스 20") } } }
struct myStaticWidgetEntryView : View { var entry: Provider.Entry var body: some View { ZStack { Color(.systemOrange) VStack { Text("최신 뉴스 20") } } } }
뷰 백그라운드의 색상을 변경하기 위해선 ZStack에서 Color를 추가해 줘야 합니다.
아래 이미지를 통해 VStack과 ZStack에 배경색을 넣었을 때 차이를 확인할 수 있습니다.
배경색을 변경했으니 이제 리스트를 추가하려고 하는데 위젯에서는 List를 지원하지 않는다고 합니다.
리스트를 추가해보면 위와 같은 모습을 볼 수 있는데 이유를 찾아보니 위젯은 스크롤을 지원하지 않아서 사용할 수 없다는 것 같습니다.
위에서 사용한 VStack을 리스트처럼 사용하도록 합니다.
struct RowView : View { let index: Int let name: String var body: some View { Text(name) Divider() } }
리스트에 행에 해당하는 뷰를 만듭니다. 텍스트는 뉴스의 타이틀을 보여줄 예정이며
Divider를 사용해 텍스트 아래 구분선을 나타냅니다.
이제 위젯의 크기에 따라 리스트를 보여주도록 하겠습니다.
struct SimpleEntry: TimelineEntry { let date: Date /// 필수 프로퍼티 let news: News }
TimelineEntry에 News Class을 추가해줬습니다. 사용하는 데이터에 따라 세팅해주면 됩니다.
func getNewData(completion: @escaping (News) -> ()) { let newsAddress: String = "http://newsapi.org/v2/top-headlines?country=kr&apiKey=75c9a397ffa14b8694f458a03c5342cf" let task = URLSession.shared.dataTask(with: URL(string: newsAddress)!) { (data, response, errpr) in if let jsonData = data { if let news = try? JSONDecoder().decode(News.self, from: jsonData) { completion(news) } } } task.resume() }
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { getNewData { (news) in let entry = SimpleEntry(date: Date(), news: news) completion(entry) } }
위젯을 추가할 때 데이터를 미리 볼 수 있도록 하였습니다.
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { getNewData { (news) in let date = Date() let entry = SimpleEntry(date: date, news: news) let nextUpdate = Calendar.current.date(byAdding: .minute, value: 15, to: date) let timeline = Timeline(entries: [entry], policy: .after(nextUpdate!)) completion(timeline) } }
struct myStaticWidgetEntryView : View { @Environment(\.widgetFamily) private var widgetFamily var entry: Provider.Entry var body: some View { ZStack { Color(.systemOrange) VStack { switch widgetFamily { case .systemSmall: Text("최신 뉴스 \(entry.news.articles?.count ?? 0)").foregroundColor(.white) case .systemMedium: if let data = entry.news.articles { ForEach(0..<4) { index in RowView(index: index, name: data[index].title!).padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)).foregroundColor(.white) } } case .systemLarge: if let data = entry.news.articles { ForEach(data, id:\.title) { item in Text(item.title!).padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)).foregroundColor(.white) Divider() } } } } } } }
위젯의 크기를 알기 위해서 @Environment(\. widgetFamily) private var widgetFamily를 선언 후 switch 문으로 분기합니다.
ForEach를 사용해 News API로 얻은 리스트를 RowView로 추가하면 리스트가 완성됩니다.
이제 홈화면에 위젯을 추가하고 로우를 선택 시 해당 선택된 로우의 정보를 뷰에서 확인할 수 있도록 하겠습니다.
struct RowView : View { let index: Int let name: String var body: some View { let url = URL(string: "widget://key?Params=\(index)")! Link(destination: url) { Text(name) Divider() } } }
Link를 추가해 각각의 리스트에서 앱의 appDelegate 또는 sceneDeleagate의 openURL 이동할 수 있도록 합니다.
위젯 전체를 눌러 딥링킹 하고자 한다면 widgetURL을 사용하면됩니다.
VStack { Text(name) Divider() }.widgetURL(URL(string: "widget://key?Params=\(index)"))
이제 sceneDeleagate로 이동해서 아래 소스를 추가해 줍니다.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { if let url = URLContexts.first?.url { if url.absoluteString.starts(with: "widget://key") { guard let urlComponents = URLComponents(string: url.absoluteString) else { return } guard let requestParams = urlComponents.queryItems?.first(where: { $0.name == "Params" })?.value else { return } guard let requestValue = urlComponents.queryItems?.first(where: { $0.name == "Value" })?.value else { return } let storyboard = UIStoryboard(name: "Main", bundle: nil) let vc = storyboard.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2 vc.data = requestParams self.window?.rootViewController = vc self.window?.makeKeyAndVisible() } } }
URL에 있던 index를 뷰 컨트롤 프로퍼티에 세팅하고 인덱스에 해당하는 뉴스를 보여주면 됩니다.
저는 인덱스만 표시하도록 했습니다.
@main struct myStaticWidget: Widget { let kind: String = "myStaticWidget" /// kind - Widget의 고유 식별자 /// provider - 새로고침할 타임라인 결정 /// StaticConfiguration 사용자가 프로퍼티를 구성할 수 없다. <- 그냥 보여주기용 /// IntentConfiguration 사용자가 프로퍼티를 구성할 수 있다. <- 보고싶은것을 선택하는 것 등 var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in myStaticWidgetEntryView(entry: entry) } .configurationDisplayName("My Widget") // 위젯 추가, 편집시 표시 이름 .description("This is an example widget.") // 위젯에 표시되는 설명 .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) // 사이즈 지원 } }
위젯이 지원하는 사이즈를 선택하려면 supportedFamilies([.systemSmall, .systemMedium, .systemLarge])를 사용하면 됩니다.
'ios' 카테고리의 다른 글
[ios - Swift] NSCache Image 사용하기 (0) 2021.02.09 [ios - Swift] App 상태 변경에 따른 Widget 초기화 (0) 2021.02.04 [ios - SwiftUI] Widget 사용하기 (1/2) (0) 2021.02.04 [ios-Swift] Share Extension 사용하기 (0) 2021.01.27 [ios - Swift] Phi Chart View (CALayer) (0) 2021.01.12