iOS14 WidgetDemo使用

1.創(chuàng)建項(xiàng)目
正常的創(chuàng)建項(xiàng)目流程,我使用的是Object-C語(yǔ)言。
創(chuàng)建項(xiàng)目完成后引入Widget Extension。

File -> New -> target-> Widget Extension ->Next

由于是加入一個(gè)新的Target煮仇,所以Widget的名字不能與項(xiàng)目名相同,也不能起成“Widget”(因?yàn)閃idget是一個(gè)已有的類名)谎仲,刪除時(shí)不能只是刪除文件還要在項(xiàng)目的Targets中刪除浙垫,起已經(jīng)刪除過(guò)一次的名字會(huì)報(bào)找不到文件的錯(cuò)誤。如果 Widget 支持用戶配置屬性(例如天氣組件郑诺,用戶可以選擇城市)夹姥,就需要勾選Include Configuration Intent這個(gè)選項(xiàng)。

image.png

創(chuàng)建后辙诞,會(huì)自動(dòng)生成5個(gè)struct和自帶的方法辙售。

2.探究自帶的方法用法
預(yù)覽視圖-Previews

代碼運(yùn)行的預(yù)覽視圖是SwiftUI新特性,會(huì)將運(yùn)行成果顯示在右邊的視圖上且支持熱更新飞涂,但是會(huì)很卡旦部,它不是Widget的必須部分,可以直接將其刪除或注釋较店。

struct WidgetView_Previews: PreviewProvider {
    static var previews: some View {
        WidgetViewEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
            .previewContext(WidgetPreviewContext(family: .systemMedium))
        
    }
}

需要注意:Widget只支持3種尺寸systemSmall (2x2)志鹃、 systemMedium (4x2)、 systemLarge(4x4)
同時(shí)泽西,一個(gè)APP可以支持多個(gè)不同的Widget。
數(shù)據(jù)提供-Provider
Provider是Widget最重要的部分缰趋,它決定了小組件的placeholder/getSnapshot/getTimeline這三種數(shù)據(jù)的顯示捧杉。在項(xiàng)目創(chuàng)建時(shí)勾選了Include Configuration Intent后的話陕见,Provider繼承自IntentTimelineProvider支持用戶自主編輯,沒(méi)有勾選則繼承自TimelineProvider不支持用戶自主編輯味抖。

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date())
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date())
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []

        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        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)
        completion(timeline)
    }
}

官方的示例代碼的意思是:顯示從現(xiàn)在開始的5個(gè)小時(shí)的每個(gè)小時(shí)的時(shí)間评甜,再顯示完之后又重新運(yùn)行一次getTimeline。所以仔涩,我們只需要控制刷新時(shí)間的Calendar.Component的Value與entries中元素的個(gè)數(shù)忍坷,并設(shè)置TimeLine的 policy 就可以控制Widget的刷新時(shí)間,次數(shù)和方法熔脂。但是佩研,這個(gè)刷新次數(shù)是有蘋果官方是有限制的,5分鐘刷新一次是極限霞揉,低于5分鐘刷新官方會(huì)覺(jué)得刷新次數(shù)太多旬薯,高頻率的刷新也會(huì)導(dǎo)致耗電量的增加。

Timeline里面有三種方式:atEnd适秩,after(date)绊序,never
atEnd: timeline 中最后一個(gè) entry 顯示后更新。timelines 方法會(huì)重新調(diào)用秽荞。
after(date): 指定日期骤公,重新更新timeline。
never:系統(tǒng)不會(huì)自動(dòng)更新扬跋,除非我們主動(dòng)通過(guò) Widget Center Api 來(lái)更新阶捆。

例子:實(shí)現(xiàn)一個(gè)按秒刷新的時(shí)鐘,為了每一秒盡可能的準(zhǔn)確刷新就應(yīng)該向entries提供0-299這300秒的300個(gè)時(shí)間數(shù)據(jù)胁住,View展示時(shí)轉(zhuǎn)換成具體到秒的字符串展示即可趁猴,運(yùn)行一個(gè)周期后再次獲取5分鐘的時(shí)間數(shù)據(jù)。但是我測(cè)試了之后彪见,發(fā)現(xiàn)在刷新了一段時(shí)間之后儡司,小組件就不在刷新了,所以余指,如果要做定時(shí)器還是有問(wèn)題捕犬。

var currentDate = Date()
            // 每5分鐘刷新一次
        let refreshDate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
        var arr:[SimpleEntry] = []
        var tempDate = Date()
            for idx in 0...300 {
                tempDate = Calendar.current.date(byAdding: .second, value: idx, to: currentDate)!
                let tempEntry = SimpleEntry(date: tempDate, configuration: configuration)
                arr.append(tempEntry)
            }
        let timeline = Timeline(entries: arr, policy: .after(refreshDate))
        completion(timeline)

也可以主動(dòng)刷新

//刷新所有 widget
WidgetCenter.shared.reloadAllTimelines

//或者

//刷新某一個(gè)widget.  xxxx 是該widget的 identifier
WidgetCenter.shared.reloadTimelines(ofKind: "xxxx")

宿主App的數(shù)據(jù)有更新時(shí),可以主動(dòng)刷新 widget UI酵镜。具體怎么做呢碉碉?對(duì)于宿主App是 OC 寫的項(xiàng)目,需要下面兩步:

建立橋接文件
一般在新建 swift 文件時(shí) Xcode 會(huì)自動(dòng)彈出是否建立 bridge 文件的彈框淮韭,選擇創(chuàng)建垢粮。
也可以自己新建一個(gè) Header file 文件,然后指定 Targets-> Swift Compiler -> Object-C Bridging Header -> Header文件
建立swift文件
WidgetCenter是 swift 的類靠粪,這里我們新建一個(gè) swift 文件作為我們的刷新工具類, 里面代碼如下:

// 導(dǎo)入 widget kit 庫(kù)
import WidgetKit
//聲明 14以上的系統(tǒng)才可用此 api
@available(iOS 14.0, *)
//定義 oc 方法
@objcMembers final class WidgetKitHelper : NSObject {
    class func reloadAllWidgets() {
       // arm64架構(gòu)真機(jī)以及模擬器可以使用
        #if arch(arm64) || arch(i386) || arch(x86_64)
            WidgetCenter.shared.reloadAllTimelines()
        #endif
    }
}

刷新的時(shí)候蜡吧,直接調(diào)用

[WidgetKitHelper reloadAllWidgets]
數(shù)據(jù)共享

我們刷新 widget 是因?yàn)橛袛?shù)據(jù)更新了所以要刷新 UI毫蚓。那 widget 如何取出宿主App 的數(shù)據(jù)進(jìn)行刷新呢?也需要兩步:

  1. 建立 App GroupApp Group 定義一個(gè)唯一標(biāo)識(shí)比如:group.com.yourcompany.xxx元潘,同時(shí),開發(fā)者證書的 Capabilities 這一項(xiàng)也要把 App Group 勾選上

  2. widget 中取出數(shù)據(jù)

UserDefaults(suiteName: "group.com.yourcompany.xxx").object(forKey: "xxxxxx")

下面是寫組件UI的地方
UI用的是SwiftUI君仆,具體語(yǔ)法可以參考這里https://gitee.com/TheAlgorithms/SwiftUI#HStack

struct WidgetViewEntryView : View {
    var entry: Provider.Entry
    static let taskDateFormat: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "HH:mm:ss"
            return formatter
    }()
    var body: some View {
        Link(destination: URL(string: "widgetDemo://799")!){
            VStack {
                Text("\(entry.date, formatter: Self.taskDateFormat)")
            }
            .frame(height: 20)
            
        }
        Link(destination: URL(string: "widgetDemo://798")!){
            VStack {
                Text("I'm a Button")
            }
            .frame(height: 20)
            
        }

    }
}

通過(guò)Link標(biāo)記控件 點(diǎn)擊對(duì)應(yīng)控件,跳轉(zhuǎn)到appdeleget里,根據(jù)傳過(guò)來(lái)的url參數(shù),執(zhí)行對(duì)應(yīng)的操作导俘。需要注意呀伙,systemSmall,小的Widget不支持這種link點(diǎn)擊,小的Widget點(diǎn)擊之后 直接跳轉(zhuǎn)進(jìn)去app。

-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{
    NSLog(@"%@",url.absoluteString);
    if ([url.scheme isEqualToString:@"widgetDemo://799"]){
        //執(zhí)行跳轉(zhuǎn)后的操作
    }
    return YES;
}

最后是小組件用戶手動(dòng)配置數(shù)據(jù)


image.png

創(chuàng)建小組件的時(shí)候,要勾選這個(gè)配置項(xiàng)参咙,intentdefinition的配置頁(yè)面,在下面的地方增加一個(gè)title屬性硫眯,String類型蕴侧,注意右邊的四個(gè)選項(xiàng)只需要勾選第2個(gè)。
image.png

回到代碼實(shí)現(xiàn)頁(yè)面两入,我們只要修改下WidgetEntryView的body里面的Text內(nèi)容净宵,從entry里面獲取到configuration配置的title屬性:
var body: some View {
//        Text(entry.date, style: .time)
        Text(entry.configuration.title == nil ? "沒(méi)有值" : entry.configuration.title!)
    }

然后 就可以實(shí)現(xiàn)小組件顯示用戶輸入文字。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末裹纳,一起剝皮案震驚了整個(gè)濱河市择葡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剃氧,老刑警劉巖敏储,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異朋鞍,居然都是意外死亡已添,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門滥酥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)更舞,“玉大人,你說(shuō)我怎么就攤上這事坎吻±虏酰” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)返奉。 經(jīng)常有香客問(wèn)我贝搁,道長(zhǎng),這世上最難降的妖魔是什么芽偏? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮弦讽,結(jié)果婚禮上污尉,老公的妹妹穿的比我還像新娘。我一直安慰自己往产,他們只是感情好被碗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仿村,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焚志。 梳的紋絲不亂的頭發(fā)上畏鼓,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天云矫,我揣著相機(jī)與錄音,去河邊找鬼挑社。 笑死巡揍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的录平。 我是一名探鬼主播缀皱,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼表箭!你這毒婦竟也來(lái)了钮莲?” 一聲冷哼從身側(cè)響起彼水,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凤覆,失蹤者是張志新(化名)和其女友劉穎盯桦,沒(méi)想到半個(gè)月后渤刃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡略号,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年玄柠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了随闪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铐伴,死狀恐怖当宴,靈堂內(nèi)的尸體忽然破棺而出泽疆,到底是詐尸還是另有隱情,我是刑警寧澤殉疼,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布瓢娜,位于F島的核電站,受9級(jí)特大地震影響虏劲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜励堡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一应结、第九天 我趴在偏房一處隱蔽的房頂上張望泉唁。 院中可真熱鬧,春花似錦、人聲如沸漩绵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瘩燥。三九已至,卻和暖如春厉膀,著一層夾襖步出監(jiān)牢的瞬間二拐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工企软, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仗哨,地道東北人铅辞。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桩卵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胜嗓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容