iOS14 Widget 開(kāi)發(fā)相關(guān)及易報(bào)錯(cuò)地方處理

首先了解下如何創(chuàng)建

Xcode -> File -> New -> Target 找到 Widget Extension

image.png

如果你的 Widget 支持用戶配置屬性,則需要勾選這個(gè)(例如天氣組件,用戶可以選擇城市)鲁森,不支持的話則不用勾選

了解下創(chuàng)建Widget后砾嫉,系統(tǒng)給我們生成的文件內(nèi)容

下面這個(gè)代碼是沒(méi)有勾選 Include Configuration Intent 的地方

Provider

// Provider浅乔,顧名思義為小組件提供信息得一個(gè)struct
struct Provider: TimelineProvider {
    public typealias Entry = SimpleEntry
    
    // 編輯屏幕時(shí)秦效,左上角選擇添加小組件時(shí)候抱究,第一次展示小組件會(huì)走這個(gè)方法
    public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {
        
    }

    // 這個(gè)方法內(nèi)可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求恢氯,拿到的數(shù)據(jù)保存在對(duì)應(yīng)的 entry 中,調(diào)用 completion 之后會(huì)到刷新小組件
    public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        // 例如這是一個(gè)網(wǎng)絡(luò)請(qǐng)求
        Network.request { data in
            let entry = SimpleEntry(date: renderDate, data: data)
            let timeline = Timeline(entries: [entry], policy: .after(nextRequestDate))
            completion(timeline)
        }
    }
}

Entry

官方解釋: A type that specifies the date to display a widget, and, optionally, indicates the current relevance of the widget’s content.

// 我的理解是就是存儲(chǔ)小組件的數(shù)據(jù)的一個(gè)東西
struct SimpleEntry: TimelineEntry {
    let date: Date
    let data: Data
}

PlacehodlerView

// 這個(gè)是一個(gè)默認(rèn)視圖鼓寺,例如網(wǎng)絡(luò)請(qǐng)求失敗勋拟、發(fā)生未知錯(cuò)誤、第一次展示小組件都會(huì)展示這個(gè)view
struct PlaceholderView : View {
    
}

WidgetEntryView

// 這個(gè)是我們需要布局小組件長(zhǎng)什么樣子的view
struct StaticWidgetEntryView : View {
    
}

主入口

@main
struct StaticWidget: Widget {
    private let kind: String = "StaticWidget"

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

支持多Widget樣式

@main
struct MainWidgets: WidgetBundle {

    @WidgetBundleBuilder
    var body: some Widget {
        Widget1()
        Widget2()
    }

}

勾選 Include Configuration Intent 之后可能出錯(cuò)的地方

如果你的app中設(shè)置了 Class Prefix 這下面這個(gè) ConfigurationIntent.self 則需要加上對(duì)應(yīng)的前綴

例如前綴是 XY 則需要修改為 XYConfigurationIntent.self

@main
struct MainWidget: Widget {
    private let kind: String = "MainWidget"

    public var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: XYConfigurationIntent.self, provider: Provider(), placeholder: PlaceholderView()) { entry in
            IntentWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}

處理Widget點(diǎn)擊事件

Widget 支持三種顯示方式妈候,分別是 systemSmall 指黎、 systemMediumsystemLarge
small 樣式只能用 widgetUrl 處理

@ViewBuilder
var body: some View {
    ZStack {
        AvatarView(entry.character)
            .widgetURL(url)
            .foregroundColor(.white)
    }
    .background(Color.gameBackground)
}

mediumlarge 可以用 Link 或者 widgetUrl 處理州丹,我們看到里面有四個(gè)相同的view,即左邊圖片杂彭,右邊文字的墓毒,這個(gè)view代碼如下(Link方式)

struct RecipeView: View {
    let recipe: RecipeModel
    
    var body: some View {
        Link(destination: URL(string: "你的網(wǎng)址")!) {
            HStack {
                WebImageView(imageUrl: recipe.squareImageUrl)
                    .frame(width: 65, height: 65)
                
                Text(recipe.adjName + recipe.name)
                    .font(.footnote)
                    .bold()
                    .foregroundColor(.black)
                    .lineLimit(3)
            }
        }
    } 
}

添加 LinkwidgetUrl 后,點(diǎn)擊每個(gè) RecipeView 都會(huì)觸發(fā)事件亲怠,這時(shí)候你需要在主項(xiàng)目中的 AppDelegate 中的如下方法進(jìn)行處理

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        
}

關(guān)于Widget中加載網(wǎng)絡(luò)圖片的時(shí)機(jī)

當(dāng)我們?cè)?code>func timeline(withcompletion)這個(gè)方法中請(qǐng)求到數(shù)據(jù)拿到圖片鏈接后所计,必須同步把圖片解析出來(lái),否則直接讓對(duì)應(yīng)的WidgetView去load url 是加載不出來(lái)的

正確的寫(xiě)法

Struct Model {
  ...
  let image: UIImage
}

func timeline(with context: Context, completion: @escaping (Timeline<LFPlanEntry>) -> ()) {
    Network.request { data in
        // 解析圖片
        var image: UIImage? = nil
        if let imageData = try? Data(contentsOf: url) {
            image = UIImage(data: imageData)
        }
        let model = Model(image: image ?? defalutImage) // 這里給個(gè)默認(rèn)圖片
        let entry = SimpleEntry(date: entryDate, data: model)
        let timeline = Timeline(entries: [entry], policy: .atEnd)
        completion(timeline)
    }
}

Struct WidgetView: View {
    
    let model: Model

    @ViewBuilder
    var body: some View {
        Image(uiImage: model.image)
                    .resizable()
    }

}

錯(cuò)誤的寫(xiě)法(直接丟url給view去加載)

struct WidgetView : View {
    
    let model: Model

    @State private var remoteImage : UIImage? = nil
    
    let defaultImage = UIImage(named: "default")!
    
    var body: some View {
        Image(uiImage: self.remoteImage ?? defaultImage)
            .onAppear(perform: fetchRemoteImage)
    }
    
    func fetchRemoteImage() {
        guard let url = URL(string: model.url) else { return }
        URLSession.shared.dataTask(with: url){ (data, response, error) in
            if let image = UIImage(data: data!){
                self.remoteImage = image
            } else {
                print(error ?? "")
            }
        }.resume()
    }
}

基于我們的app做出來(lái)的簡(jiǎn)單效果圖

效果圖

Widget相關(guān)資料

Widgets

Creating a Widget Extension

Keeping a Widget Up To Date

Making a Configurable Widget

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末团秽,一起剝皮案震驚了整個(gè)濱河市主胧,隨后出現(xiàn)的幾起案子叭首,更是在濱河造成了極大的恐慌,老刑警劉巖踪栋,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焙格,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡夷都,警方通過(guò)查閱死者的電腦和手機(jī)眷唉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)囤官,“玉大人冬阳,你說(shuō)我怎么就攤上這事〉骋” “怎么了肝陪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)刑顺。 經(jīng)常有香客問(wèn)我氯窍,道長(zhǎng),這世上最難降的妖魔是什么捏检? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任荞驴,我火速辦了婚禮,結(jié)果婚禮上贯城,老公的妹妹穿的比我還像新娘熊楼。我一直安慰自己,他們只是感情好能犯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布鲫骗。 她就那樣靜靜地躺著,像睡著了一般踩晶。 火紅的嫁衣襯著肌膚如雪执泰。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天渡蜻,我揣著相機(jī)與錄音术吝,去河邊找鬼。 笑死茸苇,一個(gè)胖子當(dāng)著我的面吹牛排苍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播学密,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼淘衙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了腻暮?” 一聲冷哼從身側(cè)響起彤守,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤毯侦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后具垫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體侈离,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年做修,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霍狰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饰及,死狀恐怖蔗坯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情燎含,我是刑警寧澤宾濒,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站屏箍,受9級(jí)特大地震影響绘梦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赴魁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一卸奉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颖御,春花似錦榄棵、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至芦岂,卻和暖如春瘪弓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背禽最。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工腺怯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人川无。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓瓢喉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親舀透。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345