• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • SwiftUI 如何創建widgets for iOS14

    標簽: java  區塊鏈  github  ios  spring

    Widgets小部件在iOS中存在很長一段時間,但iOS 14對其進行了徹底的改進,WWDC2020明確要求使用SwiftUI編寫。 iOS 14的小部件有多種類型,范圍從簡單的信息方塊到可以從其父應用程序的Siri Intents檢索和顯示信息的小部件。

    但是,其中最具吸引力的就是可以在主頁放置小部件,這意味著從技術上講,您現在可以制作可視化的“微型應用程序”。如果您發現自己一遍又一遍地執行相同的任務,例如檢查某個應用程序支持的版本或最新版本中的崩潰次數,則可以在iOS 14中創建一個小部件。

    盡管除了觸摸小部件(它會觸發應用程序啟動)之外,您無法與其進行交互,但是在它們中顯示的內容并沒有很多限制,因此您可以使用它們來開發只讀的可視化應用程序。

    開發“快速提交跟蹤器”小部件

    我發現自己偶爾去Swift倉庫,看看社區在做什么。為了讓我的生活更輕松,如何在主屏幕上直接顯示此信息?為此,我們可以向GitHub的公共API發出請求,解析信息并將其呈現給我們的小部件。

    我們可以從創建一個新的iOS項目開始-該項目的細節無關緊要,因為在本教程中,所有代碼都將在Widget的模塊內。

    創建項目后,通過轉到File-> New-> Target并選擇Widget Extension target添加一個Widget模塊。

    widgets

    確保取消選中“Include Configuration Intent”復選框,因為這涉及一項僅在本文稍后介紹的功能!生成目標后,請確保刪除示例代碼,以便我們可以逐步檢查它。


    create widget extension

    要定義Widget,您要做的就是創建一個從Widget繼承并配置其功能的結構:

    @main
    struct CommitCheckerWidget: Widget {
        private let kind: String = "CommitCheckerWidget"
    
        public var body: some WidgetConfiguration {
            StaticConfiguration(kind: kind, provider: CommitTimeline(), placeholder: PlaceholderView()) { entry in
                CommitCheckerWidgetView(entry: entry)
            }
            .configurationDisplayName("Swift's Latest Commit")
            .description("Shows the last commit at the Swift repo.")
        }
    }

    在定義此類功能之前,不會編譯此代碼,但這對于第一步很重要,因為這是Widget本身。 WidgetConfiguration返回值描述此窗口小部件是什么以及如何構建,但最重要的是,它如何獲取其內容。

    小部件類型

    StaticConfiguration WidgetConfiguration定義了無需用戶任何輸入即可自行解決的Widget。您可以在Widget的父應用中獲取任何相關數據,并將結果作為“用戶輸入”發送到Widget模塊,但是由于在配置Widget時可以進行API調用,因此如果沒有上下文,則無需這樣做請求中涉及的信息。

    另一方面,您可以使用IntentConfiguration WidgetConfiguration定義一個依賴于父應用程序中Siri Intent的Widget,它允許您構建可配置的動態Widget。例如,當使用意圖時,食品配送應用程序可以創建一個小部件,以顯示用戶最新訂單的配送狀態。這是通過讓應用程序分派一個Siri Intent(就像開發Siri快捷方式時一樣)來完成的,這些IntentConfiguration會自動將它們拾取并用于更新Widget。您可以通過在創建Widget擴展時選中intent框來創建基本的IntentConfiguration Widget,但是由于我們要做的只是解析GitHub的公共API,因此我們可以使用StaticConfiguration Widget并避免與應用程序本身進行交互。

    時間線提供者

    iOS 14的小部件顯示的內容類似于watchOS的復雜性,在某種意義上,您無需提供一直在運行的擴展程序,而是一次提供了操作系統應在整個小時,幾天內顯示的事件的“時間表”甚至幾個星期這對于“天氣”和“日歷”等應用程序很有用,您可以在其中“預測”將來將要顯示的內容,因為它們已經具有該信息。

    在我們的案例中,由于我們無法預測Swift的提交,因此我們將提供一個僅包含單個事件的時間軸-使iOS更定期地刷新Widget。

    要創建時間線,我們首先需要定義一個TimelineEntry。當預期在小部件中呈現此條目時,TimelineEntry僅需要日期,但是它也可以包含您需要的任何其他信息。在我們的例子中,我們的條目將包含我們要在小部件中顯示的提交。

    struct Commit {
        let message: String
        let author: String
        let date: String
    }
    
    struct LastCommitEntry: TimelineEntry {
        public let date: Date
        public let commit: Commit
    }

    但是在創建時間軸之前,我們需要能夠獲取此類提交。讓我們創建一個CommitLoader類,該類獲取并解析Swift的最新提交:

    struct Commit {
        let message: String
        let author: String
        let date: String
    }
    
    struct CommitLoader {
        static func fetch(completion: @escaping (Result<Commit, Error>) -> Void) {
            let branchContentsURL = URL(string: "https://api.github.com/repos/apple/swift/branches/master")!
            let task = URLSession.shared.dataTask(with: branchContentsURL) { (data, response, error) in
                guard error == nil else {
                    completion(.failure(error!))
                    return
                }
                let commit = getCommitInfo(fromData: data!)
                completion(.success(commit))
            }
            task.resume()
        }
    
        static func getCommitInfo(fromData data: Foundation.Data) -> Commit {
            let json = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
            let commitParentJson = json["commit"] as! [String: Any]
            let commitJson = commitParentJson["commit"] as! [String: Any]
            let authorJson = commitJson["author"] as! [String: Any]
            let message = commitJson["message"] as! String
            let author = authorJson["name"] as! String
            let date = authorJson["date"] as! String
            return Commit(message: message, author: author, date: date)
        }
    }

    調用fetch時,此加載程序類型將請求發送到GitHub的公共API并解析最新的提交-為我們提供消息,作者及其時間戳。現在,我們可以創建一個時間軸,以獲取最新的提交,將其添加為條目,并計劃在一段時間后進行更新。

    struct CommitTimeline: TimelineProvider {
        typealias Entry = LastCommitEntry
        /* protocol methods implemented below! */
    }

    TimelineProvider協議有兩種我們需要實現的方法:

    snapshot()-小部件的偽造信息
    TimelineProvider協議所需的snapshot()方法定義了當Widget在瞬態情況下(例如Widget選擇屏幕)出現時,應該如何配置Widget。當顯示正確的信息無關緊要時,將使用此配置:

    要創建快照配置,所有要做的就是創建并返回TimelineEntry對象的假條目。

    public func snapshot(with context: Context, completion: @escaping (LastCommitEntry) -> ()) {
        let fakeCommit = Commit(message: "Fixed stuff", author: "John Appleseed", date: "2020-06-23")
        let entry = LastCommitEntry(date: Date(), commit: fakeCommit)
        completion(entry)
    }

    timeline()-小部件的真實信息
    但是,timeline()方法定義了窗口小部件應使用的真實信息。目的是讓您返回一個時間軸實例,其中包含要顯示的所有條目,預期顯示的條目(條目的日期)以及時間軸“到期”的時間。

    由于我們的應用程序無法像Weather應用程序那樣“預測”其未來狀態,因此我們只需創建一個具有立即顯示的單個條目的時間軸即可,這可以通過將條目的日期設置為當前Date()來完成。 :

    public func timeline(with context: Context, completion: @escaping (Timeline<LastCommitEntry>) -> ()) {
        let currentDate = Date()
        let refreshDate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
    
        CommitLoader.fetch { result in
            let commit: Commit
            if case .success(let fetchedCommit) = result {
                commit = fetchedCommit
            } else {
                commit = Commit(message: "Failed to load commits", author: "", date: "")
            }
            let entry = LastCommitEntry(date: currentDate, commit: commit)
            let timeline = Timeline(entries: [entry], policy: .after(refreshDate))
            completion(timeline)
        }
    }

    時間軸的policy屬性定義了iOS何時應嘗試丟棄此時間軸并獲取新的時間軸。當前,它們可以是.never(顯示靜態內容的小部件,這些內容永遠不變)、. atEnd(顯示時間線中的最后一個條目時)或.after(Date),即顯示完后的特定時間后時間表。由于我們的時間軸只有一個條目,因此我決定使用.after告訴iOS該小部件應每5分鐘重新加載一次。

    但是請注意,Widget API的文檔指出您無法預測何時更新Widget。即使確實會在5分鐘后再次獲取時間軸本身,也無法保證iOS會同時更新視圖。從我撰寫本文的個人經驗來看,視圖實際上需要大約20分鐘的時間來更新。更新時間基于幾個因素,其中包括用戶看到窗口小部件的頻率。如果需要強制更新小部件,則可以使用主應用程序中的WidgetCenter API重新加載所有時間軸(或特定時間軸):

    WidgetCenter.shared.reloadAllTimelines()

    盡管我們的Widget不需要這樣做,但重要的是要提到時間表和條目還有其他有趣的功能。例如,可以為條目設置相關性值,這將使iOS可以確定小部件的重要性。例如,這用于確定堆棧內小部件的順序:

    struct LastCommit: TimelineEntry {
        public let date: Date
        public let commit: Commit
    
        var relevance: TimelineEntryRelevance? {
            return TimelineEntryRelevance(score: 10) // 0 - not important | 100 - very important
        }
    }

    創建小部件視圖

    現在我們已經配置了時間線,我們可以創建小部件的可視組件。我們需要創建兩個視圖:在加載時間線時顯示的占位符,以及能夠呈現時間線條目的實際Widget視圖。

    struct PlaceholderView : View {
        var body: some View {
            Text("Loading...")
        }
    }
    
    struct CommitCheckerWidgetView : View {
        let entry: LastCommitEntry
    
        var body: some View {
            VStack(alignment: .leading, spacing: 4) {
                Text("apple/swift's Latest Commit")
                    .font(.system(.title3))
                    .foregroundColor(.black)
                Text(entry.commit.message)
                    .font(.system(.callout))
                    .foregroundColor(.black)
                    .bold()
                Text("by \(entry.commit.author) at \(entry.commit.date)")
                    .font(.system(.caption))
                    .foregroundColor(.black)
                Text("Updated at \(Self.format(date:entry.date))")
                    .font(.system(.caption2))
                    .foregroundColor(.black)
            }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading)
                .padding()
                .background(LinearGradient(gradient: Gradient(colors: [.orange, .yellow]), startPoint: .top, endPoint: .bottom))
        }
    
        static func format(date: Date) -> String {
            let formatter = DateFormatter()
            formatter.dateFormat = "MM-dd-yyyy HH:mm"
            return formatter.string(from: date)
        }
    }

    您應該能夠編譯您的代碼,并且現在已經提供了所有組件,我們可以再次查看我們的配置方法,以了解如何將它們全部包裝在一起:

    @main
    struct CommitCheckerWidget: Widget {
        private let kind: String = "CommitCheckerWidget"
    
        public var body: some WidgetConfiguration {
            StaticConfiguration(kind: kind, provider: CommitTimeline(), placeholder: PlaceholderView()) { entry in
                CommitCheckerWidgetView(entry: entry)
            }
            .configurationDisplayName("Swift's Latest Commit")
            .description("Shows the last commit at the Swift repo.")
        }
    }

    我們創建了一個靜態小部件,該小部件可從CommitTimeline獲取其內容,具有PlaceholderView作為占位符,并在準備顯示條目時生成CommitCheckerWidgetView。

    在運行我們的應用程序并將小部件添加到我們的家中之后,我們現在可以看到一個自動更新的Swift提交顯示器!

    允許用戶配置要可視化的倉庫/分支

    如前所述,iOS 14的新API還支持與Siri Intent綁定的Widget,從而使您可以創建可由用戶配置的動態Widget。我們可以有一個基于意圖的Widget,允許用戶直接從Widget本身配置要觀看的倉庫。

    要創建基于意圖的Widget,我們首先需要一個Siri意圖。在您的窗口小部件擴展中,添加一個SiriKit Intent Definition File。

    為了允許用戶查看任何回購或分支的提交,讓我們創建一個支持account,回購和分支屬性的LastCommitIntent。確保也選中“小部件的意圖”框。

    可以將Widgets與任何捐贈的Siri Intent的數據一起使用,但是魔術之處在于不需要這樣做。如果該意圖具有窗口小部件功能(如我們創建的那樣),則您可以直接在窗口小部件上設置參數,稍后我們將看到。

    升級Widget之前,請確保我們的代碼支持從其他存儲庫中獲取提交。讓我們升級時間線條目以支持回購配置:

    struct RepoBranch {
        let account: String
        let repo: String
        let branch: String
    }
    
    struct LastCommit: TimelineEntry {
        public let date: Date
        public let commit: Commit
        public let branch: RepoBranch
    }

    從這里,我們可以升級fetcher的fetch()方法,以從任何存儲庫下載任何分支:

    static func fetch(account: String, repo: String, branch: String, completion: @escaping (Result) -> Void) {
        let branchContentsURL = URL(string: "https://api.github.com/repos/\(account)/\(repo)/branches/\(branch)")!
        // ...
    }

    如前所述,基于意圖的窗口小部件需要使用IntentConfiguration,它與我們之前的靜態方法的唯一主要區別在于,我們必須提供此窗口小部件鏈接到的意圖。讓我們更新小部件以使用IntentConfiguration和LastCommitIntent:

    @main
    struct CommitCheckerWidget: Widget {
        private let kind: String = "CommitCheckerWidget"
    
        public var body: some WidgetConfiguration {
            IntentConfiguration(kind: kind, intent: LastCommitIntent.self, provider: CommitTimeline(), placeholder: PlaceholderView()) { entry in
                CommitCheckerWidgetView(entry: entry)
            }
            .configurationDisplayName("A Repo's Latest Commit")
            .description("Shows the last commit at the a repo/branch combination.")
        }
    }

    我們必須進行的另一項修改是更新時間軸以從IntentTimelineProvider繼承,而不是從TimelineProvider繼承。它們的工作方式大致相同,不同之處在于intents變體提供對我們intent實例的訪問,從而使我們能夠掌握用戶所做的任何自定義。在這種情況下,我們將更新snapshot()以另外返回偽造的倉庫和我們的時間軸方法以獲取用戶的倉庫配置并使用這些參數來獲取提交。

    struct CommitTimeline: IntentTimelineProvider {
        typealias Entry = LastCommit
        typealias Intent = LastCommitIntent
    
        public func snapshot(for configuration: LastCommitIntent, with context: Context, completion: @escaping (LastCommit) -> ()) {
            let fakeCommit = Commit(message: "Fixed stuff", author: "John Appleseed", date: "2020-06-23")
            let entry = LastCommit(
                date: Date(),
                commit: fakeCommit,
                branch: RepoBranch(
                    account: "apple",
                    repo: "swift",
                    branch: "master"
                )
            )
            completion(entry)
        }
    
        public func timeline(for configuration: LastCommitIntent, with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
            let currentDate = Date()
            let refreshDate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
    
            guard let account = configuration.account,
                  let repo = configuration.repo,
                  let branch = configuration.branch
            else {
                let commit = Commit(message: "Failed to load commits", author: "", date: "")
                let entry = LastCommit(date: currentDate, commit: commit, branch: RepoBranch(
                    account: "???",
                    repo: "???",
                    branch: "???"
                ))
                let timeline = Timeline(entries: [entry], policy: .after(refreshDate))
                completion(timeline)
                return
            }
    
            CommitLoader.fetch(account: account, repo: repo, branch: branch) { result in
                let commit: Commit
                if case .success(let fetchedCommit) = result {
                    commit = fetchedCommit
                } else {
                    commit = Commit(message: "Failed to load commits", author: "", date: "")
                }
                let entry = LastCommit(date: currentDate, commit: commit, branch: RepoBranch(
                    account: account,
                    repo: repo,
                    branch: branch
                ))
                let timeline = Timeline(entries: [entry], policy: .after(refreshDate))
                completion(timeline)
            }
        }
    }

    盡管此代碼有效,但我們的視圖仍將“ apple / swift”硬編碼到其中。讓我們對其進行更新以使用條目現在擁有的新參數:

    struct RepoBranchCheckerEntryView : View {
        var entry: Provider.Entry
    
        var body: some View {
            VStack(alignment: .leading, spacing: 4) {
                Text("\(entry.branch.account)/\(entry.branch.repo)'s \(entry.branch.branch) Latest Commit")
                    .font(.system(.title3))
                    .foregroundColor(.black)
                Text("\(entry.commit.message)")
                    .font(.system(.callout))
                    .foregroundColor(.black)
                    .bold()
                Text("by \(entry.commit.author) at \(entry.commit.date)")
                    .font(.system(.caption))
                    .foregroundColor(.black)
                Text("Updated at \(Self.format(date:entry.date))")
                    .font(.system(.caption2))
                    .foregroundColor(.black)
            }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading)
                .padding()
                .background(LinearGradient(gradient: Gradient(colors: [.orange, .yellow]), startPoint: .top, endPoint: .bottom))
        }
    
        static func format(date: Date) -> String {
            let formatter = DateFormatter()
            formatter.dateFormat = "MM-dd-yyyy HH:mm"
            return formatter.string(from: date)
        }
    }

    現在,運行您的應用程序并檢查Widget。您看到的回購配置將由您在intents文件中添加的默認值確定,但是如果您長按Widget并單擊Edit(編輯)按鈕,現在將能夠自定義Intent的參數并更改獲取的回購!

    版權聲明:本文為iCloudEnd原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/iCloudEnd/article/details/107333710

    智能推薦

    iOS14:AirPods Auto Switching

    一、AirPods Auto Switching 先看幾張發布會截圖。 AirPods現在可以在設備間無縫切換,無需手動操作。 比如: 你剛玩播客,拿起iPad打算看個電視節目,AirPods會神奇的切換過去; 之后你在Mac上開視頻會議,AirPods會再一次自動切換過去; 如果有一通電話打進來,AirPods的音頻會切換回手機。 二、支持的AirPods // Apple官網注釋 Works ...

    iOS14適配方案

    1. 隱私適配 iOS14最重要的更新之一:用戶隱私和安全。 (1) 廣告標識符IDFA 廣告標識符IDFA全稱Identity for Advertisers,用來標記用戶以便于投放廣告、個性化推薦等。 更新前后區別如下: IDFA權限提示:iOS13左、iOS14右 1> iOS13系統IDFA iOS13及以前,系統會默認為用戶 開啟 廣告追蹤權限。 通過以下代碼即...

    【QingQing】iOS14 適配匯總

    目錄 1 UIDatePicker 時間選擇器 2 UITableViewCell 3 粘貼板問題 4 權限問題 4.1 相冊 - PHPhotoLibraryPreventAutomaticLimitedAccessAlert 4.2 定位  - NSLocationTemporaryUsageDescriptionDictionary...

    iOS14 UIDatePicker的變化

    iOS14 UIDatePicker的變化 手機系統更新到iOS14之后,在選擇日期的時候變成這樣了  更新之前是這樣的 這是因為UIDatePicker 增加了pickerStyle,需要設置preferredDatePickerStyle = UIDatePickerStyleWheels才會和以前一樣,并且現在對frame的寬高設置已經不生效了,會采用系統默認的寬高。 prefer...

    iOS 深入理解SwiftUI

    文章目錄 1. SwiftUI的優缺點 2. 語法細節-聲明式語法 3. 實時預覽 4. Xcode Library 5. Switch Case Support 6. Data Flow 數據流 6.1 數據處理的基本原則 6.2 數據流工具 6.2.1 Property 相對簡單, 在View內部定義常量, 變量, 之后在使用 6.2.2 @State 6.2.3 @Binding 6.2.4...

    猜你喜歡

    SwiftUI List 如何顯示dictionary

    本文價值與收獲 看完本文后,您將能夠作出下面的界面 看完本文您將掌握的技能 list基礎使用 顯示dictionary 代碼 技術交流 QQ:3365059189 SwiftUI技術交流QQ群:518696470...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡