前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS17适配指南之Widget

iOS17适配指南之Widget

作者头像
YungFan
发布2023-08-24 11:21:05
7910
发布2023-08-24 11:21:05
举报
文章被收录于专栏:学海无涯学海无涯

介绍

  • 增加containerBackground修饰符可以设置 Widget 的背景。
  • 增加widgetContentMargins环境变量可以设置 Widget 的边距。
  • 重要更新:可以通过AppIntent在不打开 App 的情况下进行交互操作,但交互的 View 目前仅支持 Button 与 Toggle。
代码语言:javascript
复制
extension Button {
    public init<I: AppIntent>(
        intent: I,
        @ViewBuilder label: () -> Label
    )
}

extension Toggle {
    public init<I: AppIntent>(
        isOn: Bool,
        intent: I,
        @ViewBuilder label: () -> Label
    )
}

案例

效果

效果图.gif

实现

代码语言:javascript
复制
import AppIntents
import Foundation
import SwiftUI
import WidgetKit

// MARK: - Model
class Counter {
    @AppStorage("count", store: UserDefaults(suiteName: "Widget2023")) static var count = 0

    static func incrementCount() {
        count += 1
    }

    static func decrementCount() {
        count -= 1
    }

    static func currentCount() -> Int {
        return count
    }
}



// MARK: - AppIntent
struct CountIntent: AppIntent {
    static var title: LocalizedStringResource = "CountIntent"
    static var description: IntentDescription = IntentDescription("CountIntent")

    // AppIntent的输入参数
    @Parameter(title: "isIncrement")
    var isIncrement: Bool

    init() {
    }

    init(isIncrement: Bool) {
        self.isIncrement = isIncrement
    }

    func perform() async throws -> some IntentResult {
        if isIncrement {
            Counter.incrementCount()
        } else {
            Counter.decrementCount()
        }
        return .result()
    }
}



// 宿主App
struct ContentView: View {
    @Environment(\.scenePhase) private var phase
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Count: \(count)")
                .font(.largeTitle)
                .foregroundStyle(.primary)

            HStack {
                Button {
                    count += 1
                    Counter.incrementCount()
                    WidgetCenter.shared.reloadAllTimelines()
                } label: {
                    Image(systemName: "plus")
                        .font(.largeTitle)
                }

                Button {
                    count -= 1
                    Counter.decrementCount()
                    WidgetCenter.shared.reloadAllTimelines()
                } label: {
                    Image(systemName: "minus")
                        .font(.largeTitle)
                }
            }
        }
        .padding()
        .onChange(of: phase) {
            count = Counter.currentCount()
        }
    }
}



// MARK: - Widget
struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), count: "\(Counter.currentCount())")
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        let entry = SimpleEntry(date: Date(), count: "\(Counter.currentCount())")
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
        let timeline = Timeline(entries: [SimpleEntry(date: Date(), count: "\(Counter.currentCount())")], policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let count: String
}

struct CountWidgetEntryView: View {
    // iOS17新增环境变量,设置边距
    @Environment(\.widgetContentMargins) var margins
    var entry: Provider.Entry

    var body: some View {
        VStack {
            Text("Count: \(entry.count)")

            HStack {
                // 交互
                Button(intent: CountIntent(isIncrement: true)) {
                    Image(systemName: "plus.circle")
                }

                Button(intent: CountIntent(isIncrement: false)) {
                    Image(systemName: "minus.circle")
                }
            }
            .font(.largeTitle)
        }
        .containerBackground(.fill.tertiary, for: .widget) // iOS17新增,设置小组件背景
        .padding(.top, margins.top) // 设置顶部边距
    }
}

struct CountWidget: Widget {
    let kind: String = "CountWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            CountWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("CountWidget")
        .description("This is a CountWidget.")
    }
}

// MARK: - 预览
#Preview(as: .systemSmall) {
    CountWidget()
} timeline: {
    SimpleEntry(date: .now, count: "99")
}

#Preview(as: .systemMedium) {
    CountWidget()
} timeline: {
    SimpleEntry(date: .now, count: "99")
}

#Preview(as: .systemLarge) {
    CountWidget()
} timeline: {
    SimpleEntry(date: .now, count: "99")
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 案例
    • 效果
      • 实现
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档