最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

SwiftUI學(xué)習(xí)100天(Day64 - 項目 13,第三部分)

2023-03-09 12:00 作者:愛上樹の蝸牛  | 我要投稿

原創(chuàng)鏈接:https://www.hackingwithswift.com/100/swiftui

以下內(nèi)容僅供學(xué)習(xí)參考:

信不信由你,在我們進(jìn)入實(shí)施階段之前,我們還有一天的時間來研究這個項目的技術(shù),我把最難的事情留到了最后。

今天,你將了解 SwiftUI 如何與 UIKit 協(xié)同工作的一些復(fù)雜情況。如果你之前曾經(jīng)使用過 UIKit,那么這些事情不會太費(fèi)力,但如果 UIKit 對你來說是新手,那么今天可能會像你第一次在 Swift 中遇到閉包時一樣受到傷害。對真的。

堅持下去!今天之后我們將開始將所有這些概念付諸實(shí)踐,這樣你就離真正有趣的部分很近了。從郵票中汲取靈感——正如作家喬什·比林斯 (Josh Billings) 曾經(jīng)打趣道,“它的用處在于能夠堅持一件事,直到它到達(dá)那里?!?/p>

今天你只有兩個主題要完成,你將在其中了解協(xié)調(diào)器、委托、NSObject選擇@objc器和其他在夜間出現(xiàn)的問題。


使用協(xié)調(diào)器管理 SwiftUI 視圖控制器

之前我們研究了如何使用UIViewControllerRepresentable包裝 UIKit 視圖控制器,以便它可以在 SwiftUI 中使用,特別關(guān)注PHPickerViewController.?然而,我們遇到了一個問題:雖然我們可以顯示圖像選擇器,但我們無法響應(yīng)用戶選擇圖像或按下取消。

SwiftUI 對此的解決方案稱為協(xié)調(diào)器,這對于來自 UIKit 背景的人來說有點(diǎn)混亂,因為我們有一個設(shè)計模式也稱為協(xié)調(diào)器,它執(zhí)行完全不同的角色。需要明確的是,SwiftUI 的協(xié)調(diào)器與許多開發(fā)人員在 UIKit 中使用的協(xié)調(diào)器模式完全不同,因此如果你以前使用過該模式,請將其從你的大腦中拋棄以避免混淆!

SwiftUI 的協(xié)調(diào)器旨在充當(dāng) UIKit 視圖控制器的委托。請記住,“委托”是響應(yīng)其他地方發(fā)生的事件的對象。例如,UIKit 允許我們將委托對象附加到其文本字段視圖,當(dāng)用戶鍵入任何內(nèi)容、按下回車鍵等時,該委托將收到通知。這意味著 UIKit 開發(fā)人員可以修改他們的文本字段的行為方式,而無需創(chuàng)建他們自己的自定義文本字段類型。

在 SwiftUI 中使用協(xié)調(diào)器需要你了解一些 UIKit 的工作方式,這并不奇怪,因為我們實(shí)際上是在集成 UIKit 的視圖控制器。因此,為了演示這一點(diǎn),我們將升級我們的ImagePicker視圖,以便它可以在用戶選擇圖像或按下取消時進(jìn)行報告。

提醒一下,這是我們現(xiàn)在擁有的代碼:

我們將一步一步來,因為這里有很多東西要吸收——如果你需要一些時間來理解,不要難過,因為協(xié)調(diào)員在你第一次遇到他們時真的不容易。

首先,在結(jié)構(gòu)中添加這個嵌套類ImagePicker

是的,它需要是一個類,你稍后會看到。它不需要是一個嵌套類,盡管這是一個好主意,因為它巧妙地封裝了功能——如果沒有嵌套類,如果你有很多視圖控制器和協(xié)調(diào)器都混在一起,那將會很混亂。

即使該類位于UIViewControllerRepresentable結(jié)構(gòu)內(nèi)部,SwiftUI 也不會自動將其用于視圖的協(xié)調(diào)器。相反,我們需要添加一個名為 makeCoordinator()的新方法,如果我們實(shí)現(xiàn)它,SwiftUI 會自動調(diào)用它。所有這一切需要做的就是創(chuàng)建和配置我們Coordinator類的實(shí)例,然后將其返回。

現(xiàn)在我們的Coordinator類沒有做任何特別的事情,所以我們可以通過將這個方法添加到ImagePicker結(jié)構(gòu)中來發(fā)送一個:

到目前為止我們所做的是創(chuàng)建一個ImagePicker結(jié)構(gòu)體,他知道如何創(chuàng)建PHPickerViewController,現(xiàn)在我們只是告訴ImagePicker它應(yīng)該有一個協(xié)調(diào)器來處理來自那個PHPickerViewController的通信。

下一步是告訴PHPickerViewController當(dāng)事情發(fā)生時它應(yīng)該告訴我們的協(xié)調(diào)員。這只需要 makeUIViewController()中的一行代碼,所以直接在該return picker行之前添加:

該代碼無法編譯,但在我們修復(fù)它之前,我想花點(diǎn)時間深入了解剛剛發(fā)生的事情。

我們不稱呼makeCoordinator()自己;SwiftUI 在創(chuàng)建 ImagePicker的實(shí)例時自動調(diào)用它。更好的是,SwiftUI 自動將它創(chuàng)建的協(xié)調(diào)器與我們的ImagePicker結(jié)構(gòu)體相關(guān)聯(lián),這意味著當(dāng)它調(diào)用makeUIViewController(),updateUIViewController()會自動將該協(xié)調(diào)器對象傳遞給我們。

因此,我們剛剛編寫的代碼行告訴 Swift 使用剛剛創(chuàng)建的協(xié)調(diào)器作為PHPickerViewController.?這意味著只要照片選擇器控制器內(nèi)部發(fā)生某些事情——即,當(dāng)用戶選擇圖像或按下取消——它就會將該操作報告給我們的協(xié)調(diào)器。

我們的代碼無法編譯的原因是 Swift 正在檢查我們的協(xié)調(diào)器類是否能夠充當(dāng) PHPickerViewController的委托,發(fā)現(xiàn)它不能,因此拒絕進(jìn)一步構(gòu)建我們的代碼。為了解決這個問題,我們需要修改我們的Coordinator類:

對此:

這做了三件事:

  1. 它使類繼承自NSObject,這是 UIKit 中幾乎所有內(nèi)容的父類。NSObject允許 Objective-C 在運(yùn)行時詢問對象它支持什么功能,這意味著照片選擇器可以說“嘿,用戶選擇了一張圖像,你想做什么?”

  2. 它使類符合PHPickerViewControllerDelegate協(xié)議,這就是添加檢測用戶何時選擇圖像的功能。(NSObject讓 Objective-C檢查功能;這個協(xié)議實(shí)際上提供了它。)

  3. 它會阻止我們的代碼編譯,因為我們已經(jīng)說過該類符合PHPickerViewControllerDelegate但我們還沒有實(shí)現(xiàn)該協(xié)議所需的一個方法。

盡管如此,至少現(xiàn)在你可以明白為什么我們需要使用類?Coordinator:我們需要繼承自NSObject以便 Objective-C 可以查詢我們的協(xié)調(diào)器以查看它支持什么功能。

在這一點(diǎn)上,我們有一個ImagePicker的結(jié)構(gòu)體,它包裝了一個PHPickerViewController,并且我們已經(jīng)配置了圖像選擇器控制器,以便在發(fā)生有趣的事情時與我們的Coordinator類交談。

最后一步是實(shí)現(xiàn)PHPickerViewControllerDelegate協(xié)議的一個必需方法,當(dāng)用戶完成選擇時將調(diào)用該方法。這可能意味著我們有一張圖片,或者用戶按下了取消鍵,所以我們需要做出適當(dāng)?shù)捻憫?yīng)。

如果我們暫時將 UIKit 放在一邊并考慮純功能,我們想要的是ImagePicker將該圖像報告給首先使用選擇器的任何人。我們在ContentView的結(jié)構(gòu)體的工作表中展示ImagePicker,我們希望無論選擇什么圖像都可以得到它,然后關(guān)閉工作表。

我們在這里需要的是 SwiftUI 的@Binding屬性包裝器,它允許我們創(chuàng)建一個綁定ImagePicker到任何創(chuàng)建它的對象。這意味著我們可以在我們的圖像選擇器中設(shè)置綁定值,并讓它實(shí)際更新存儲在其他地方的值——在ContentView中,例如。

因此,將此屬性添加到ImagePicker

現(xiàn)在,我們剛剛將該屬性添加到ImagePicker,但我們需要在我們的Coordinator類中訪問它,因為這是在選擇圖像時將被通知的那個。

與其將數(shù)據(jù)向下傳遞一個級別,不如告訴協(xié)調(diào)器它的父級是什么,這樣它就可以直接修改那里的值。這意味著向類添加一個ImagePicker屬性和關(guān)聯(lián)的初始化程序Coordinator,如下所示:

現(xiàn)在我們可以修改makeCoordinator(),以便它將ImagePicker結(jié)構(gòu)傳遞給協(xié)調(diào)器,如下所示:

此時你的整個ImagePicker結(jié)構(gòu)應(yīng)該是這樣的:

終于,我們準(zhǔn)備好實(shí)際讀取來自我們PHPickerViewController的響應(yīng),這是通過實(shí)現(xiàn)一個具有非常特定名稱的方法來完成的。Swift 將在我們的Coordinator類中查找此方法,因為它是圖像選擇器的委托,因此請確保將其添加到此處。

方法名稱很長,需要完全正確才能讓 UIKit 找到它,但 Xcode 可以幫助我們完成自動完成。因此,單擊錯誤行上的紅色六邊形,然后單擊“修復(fù)”以添加此方法存根:

該方法接收我們關(guān)心的兩個對象:用戶與之交互的選擇器視圖控制器,以及用戶選擇的數(shù)組,因為可以讓用戶一次選擇多個圖像。

我們的工作是做三件事:

  1. 告訴選擇器解雇自己。

  2. 如果用戶沒有做出選擇則退出——如果他們點(diǎn)擊了取消。

  3. 否則,查看用戶的結(jié)果是否包含UIImage我們可以實(shí)際加載的內(nèi)容,如果是,則將其放入parent.image屬性中。

因此,將“代碼”占位符替換為:

注意我們?nèi)绾涡枰愋娃D(zhuǎn)換UIImage——那是因為我們提供的數(shù)據(jù)在理論上可以是任何東西。是的,我知道我們特別要求提供照片,但PHPickerViewControllerDelegate對任何類型的媒體都調(diào)用相同的方法,這就是我們需要小心的原因。

在這一點(diǎn)上,我敢打賭你真的錯過了 SwiftUI 的美麗簡單,所以你會很高興知道我們終于完成了ImagePicker結(jié)構(gòu)——它做了我們現(xiàn)在需要的一切。

所以,最后我們可以返回到 ContentView.swift。這是我們之前離開它的方式:

要將我們的ImagePicker視圖集成到其中,我們需要首先添加另一個@State可以傳遞到選擇器中的圖像屬性:

我們現(xiàn)在可以更改我們的sheet()修改器以將該屬性傳遞給我們的圖像選擇器,因此它會在選擇圖像時更新:

接下來,我們需要一個可以在該屬性更改時調(diào)用的方法。請記住,我們不能在這里使用普通的屬性觀察器,因為 Swift 會忽略對綁定的更改,因此我們將編寫一個方法來檢查inputImage是否有值,如果有,則使用它為image屬性分配一個新Image視圖.

將此方法添加到ContentView現(xiàn)在:

現(xiàn)在我們可以使用onChange()修飾符在選擇新圖像時調(diào)用loadImage()- 將其放在sheet()修飾符下方:

我們完成了!繼續(xù)運(yùn)行該應(yīng)用程序并嘗試一下——你應(yīng)該能夠點(diǎn)擊按鈕,瀏覽你的照片庫,然后選擇一張照片。當(dāng)發(fā)生這種情況時,照片選擇器應(yīng)該會消失,你選擇的圖像將顯示在下方。

我意識到此時你可能厭倦了 UIKit 和協(xié)調(diào)器,但在我們繼續(xù)之前我想總結(jié)一下完整的過程:

  • 我們創(chuàng)建了一個符合UIViewControllerRepresentable的SwiftUI視圖.

  • 我們給了它一個makeUIViewController()的方法創(chuàng)建某種 UIViewController在我們的示例中是一個PHPickerViewController.

  • 我們添加了一個嵌套Coordinator類作為 UIKit 視圖控制器和我們的 SwiftUI 視圖之間的橋梁。

  • 我們?yōu)樵搮f(xié)調(diào)器提供了一個didFinishPicking方法,該方法將在選擇圖像時由 iOS 觸發(fā)。

  • 最后,我們給了我們ImagePicker一個@Binding屬性,以便它可以將更改發(fā)送回父視圖。

對于它的價值,在你使用過協(xié)調(diào)器一次之后,第二次和隨后的時間會更容易,但是如果你現(xiàn)在發(fā)現(xiàn)整個系統(tǒng)非常莫名其妙,我不會責(zé)怪你。

別太擔(dān)心——我們很快就會再次回到這個話題,所以你會有足夠的時間練習(xí)。這也意味著你不應(yīng)該刪除你的 ImagePicker.swift 文件,因為這是你將在這個項目和你創(chuàng)建的其他項目中使用的另一個有用的組件。


如何將圖像保存到用戶的照片庫

在我們完成這個項目的技術(shù)之前,我們需要解決最后一個 UIKit 的樂趣:一旦我們處理了用戶的圖像,我們就會返回UIImage,但是我們需要一種方法來將處理過的圖像保存到用戶的照片庫。這使用了一個名為 UIImageWriteToSavedPhotosAlbum()的 UIKit 函數(shù),其最簡單的形式使用起來很簡單,但為了使其有用,你需要重新研究 UIKit。至少它會讓你真正體會到 SwiftUI 有多好!

在我們編寫任何代碼之前,我們需要做一些新的事情:我們需要為我們的項目添加一個配置選項。我們構(gòu)建的每個項目都有一大堆這樣的東西,描述我們支持的界面方向、我們應(yīng)用程序的版本號和其他固定數(shù)據(jù)。這不是代碼:這些選項必須全部提前聲明,在一個單獨(dú)的文件中,這樣系統(tǒng)就可以讀取它們而無需運(yùn)行我們的應(yīng)用程序。

這些選項都位于 Xcode 中的特定位置,除非你知道自己在做什么,否則很難找到:

  1. 在 Project Navigator 中,選擇樹中的頂部項目。它將包含你的項目名稱 Instafilter。

  2. 你會看到 Instafilter 列在 PROJECT 和 TARGETS 下。請在目標(biāo)下選擇它。

  3. 現(xiàn)在你會在頂部看到一堆選項卡,包括“常規(guī)”、“簽名和功能”等 - 從那里選擇“信息”。

你可以在此處為你的項目添加一系列配置選項,但現(xiàn)在我們需要一個特定選項。你看,寫入照片庫是一項受保護(hù)的操作,這意味著我們不能在沒有用戶明確許可的情況下執(zhí)行此操作。iOS 將負(fù)責(zé)請求許可并檢查響應(yīng),但我們需要提供一個簡短的字符串來向用戶解釋我們?yōu)槭裁匆紫葘懭雸D像。

要添加你的權(quán)限字符串,請右鍵單擊任何現(xiàn)有選項,然后選擇添加行。你會看到一個可供選擇的選項下拉列表——我希望你向下滾動并選擇“隱私 - 照片庫添加使用說明”。對于右側(cè)的值,請輸入文本“我們要保存過濾后的照片”。

完成后,我們現(xiàn)在可以使用該UIImageWriteToSavedPhotosAlbum()方法寫出圖片。我們之前的工作中已經(jīng)有了這個loadImage()方法:

我們可以修改它,讓它立即保存加載的圖像,有效地創(chuàng)建一個副本。將此行添加到方法的末尾:

就是這樣 - 每次你導(dǎo)入圖像時,我們的應(yīng)用程序都會將其保存回照片庫。第一次嘗試時,iOS 會自動提示用戶寫照片的權(quán)限,并顯示我們添加到配置選項中的字符串。

現(xiàn)在,你可能會看著它并認(rèn)為“這很簡單!”?你是對的。但之所以容易,是因為我們做了盡可能少的工作:我們將要保存的圖像作為第一個參數(shù)提供給UIImageWriteToSavedPhotosAlbum()?,然后nil作為其他三個參數(shù)提供。

這些nil參數(shù)很重要,或者至少前兩個很重要:它們告訴 Swift 在保存完成時應(yīng)該調(diào)用什么方法,這反過來會告訴我們保存操作是成功還是失敗。如果你不關(guān)心那個,那你就完了——三個值都是nil是可以的。但請記?。河脩艨梢跃芙^訪問他們的照片庫,因此如果你沒有發(fā)現(xiàn)保存錯誤,他們會想知道為什么你的應(yīng)用無法正常運(yùn)行。

UIKit 需要兩個參數(shù)來知道要調(diào)用哪個函數(shù)的原因是因為這段代碼很舊——比 Swift 老得多,事實(shí)上,它甚至比 Objective-C 的閉包等價物還要早。因此,它使用了一種完全不同的函數(shù)引用方式:在引用第一個nil時,我們應(yīng)該指向一個對象,在引用第二個nil時,我們應(yīng)該指向要調(diào)用的方法的名稱。

如果這聽起來很糟糕,恐怕你只知道一半的故事。你看,這兩個參數(shù)都有其自身的復(fù)雜性:

  • 我們提供的對象必須是類,而且必須繼承自NSObject。這意味著我們無法指向 SwiftUI 視圖結(jié)構(gòu)。

  • 該方法作為方法名稱提供,而不是實(shí)際方法。Objective-C 使用此方法名稱在運(yùn)行時查找實(shí)際代碼,然后可以運(yùn)行這些代碼。該方法需要有一個特定的簽名(參數(shù)列表),否則我們的代碼將無法工作。

但是等等:還有更多!出于性能原因,Swift 不喜歡以 Objective-C 可以讀取的方式生成代碼——整個“在運(yùn)行時查找方法”的東西非常簡潔,但也非常慢。因此,在編寫方法名稱時,我們需要做兩件事:

  1. 使用一個名為#selector的特殊編譯器指令標(biāo)記該方法,它要求 Swift 確保方法名稱存在于我們所說的位置。

  2. 添加一個調(diào)用@objc到方法的屬性,它告訴 Swift 生成可以被 Objective-C 讀取的代碼。

你知道的,在我轉(zhuǎn)向 SwiftUI 之前我寫了十多年的 UIKit 代碼,并且已經(jīng)寫下所有這些解釋使得這個舊的 API 看起來像是反人類罪??杀氖牵褪沁@樣,我們堅持下去。

在向你展示代碼之前,我想提一下第四個參數(shù)。所以,第一個是要保存的圖像,第二個是應(yīng)該通知保存結(jié)果的對象,第三個是應(yīng)該運(yùn)行的對象上的方法,然后是第四個。我們不會在這里使用它,但你需要知道它的作用:我們可以在這里提供任何類型的數(shù)據(jù),當(dāng)我們的完成方法被調(diào)用時,它會傳回給我們。

這就是 UIKit 所說的“上下文”,它可以幫助你將一個圖像保存操作與另一個圖像保存操作區(qū)分開來。你可以在這里提供任何你想要的東西,所以 UIKit 使用你能想象到的最不干涉的類型:一塊原始的內(nèi)存塊,Swift 對此不做任何保證。這在 Swift 中有其自己的特殊類型名稱:UnsafeRawPointer。老實(shí)說,如果不是因為我們不得不在這里使用它,我甚至不會告訴你它的存在,因為在你的應(yīng)用程序開發(fā)生涯的這個階段,它并不是真正有用的。

無論如何,這已經(jīng)綽綽有余了。在你決定放棄這個項目并直接進(jìn)入下一個項目之前,讓我們把它結(jié)束并完成。

正如我所說,要將圖像寫入照片庫并讀取響應(yīng),我們需要某種繼承自NSObject.?在里面,我們需要一個帶有精確簽名的方法,并用 @objc標(biāo)記,然后我們可以從 UIImageWriteToSavedPhotosAlbum()調(diào)用它

將所有這些放在一起,請創(chuàng)建一個名為 ImageSaver.swift 的新 Swift 文件,將其基礎(chǔ)導(dǎo)入更改為 SwiftUI,然后為其提供以下代碼:

有了它,我們現(xiàn)在可以從 ContentView中使用它,如下所示:

如果你現(xiàn)在運(yùn)行代碼,你應(yīng)該看到“保存完成!”?選擇圖像后會立即輸出消息,但你還會看到系統(tǒng)提示你授予寫入照片庫的權(quán)限。

是的,考慮到它需要多少解釋,這是非常少的代碼,但從好的方面來說,完成了這個項目的概述,所以在很長一段時間(很長,很長?。┳詈笪覀兛梢赃M(jìn)入實(shí)際的實(shí)現(xiàn)。

請繼續(xù)并將你的項目恢復(fù)到默認(rèn)狀態(tài),這樣我們就有了一個干凈的工作狀態(tài),但我希望你保留ImagePickerImageSaver- 這兩個項目稍后都會用到,它們在其他項目中也很有用你將來可能創(chuàng)建的項目。


SwiftUI學(xué)習(xí)100天(Day64 - 項目 13,第三部分)的評論 (共 條)

分享到微博請遵守國家法律
太仆寺旗| 桦川县| 娱乐| 达日县| 班玛县| 利津县| 宁陵县| 东山县| 信宜市| 留坝县| 贵港市| 文安县| 峨眉山市| 巫山县| 辰溪县| 济南市| 尉犁县| 慈溪市| 铜山县| 阿拉尔市| 济宁市| 常山县| 张家口市| 定襄县| 葵青区| 彩票| 中超| 汉寿县| 长寿区| 祁阳县| 龙川县| 连州市| 南京市| 苍南县| 凉城县| 石林| 德兴市| 肥东县| 环江| 新源县| 黑河市|