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

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

SwiftUI學(xué)習(xí)100天(Day58 - 項(xiàng)目 12,第二部分)

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

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

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

今天我們將推進(jìn)更高級(jí)的Core Data技術(shù)——在功能和實(shí)用性方面真正讓應(yīng)用程序脫穎而出的技術(shù)。其中一些將需要一些時(shí)間來學(xué)習(xí),特別是因?yàn)楫?dāng)我們更多地深入到 Core Data 中時(shí),你將開始更多地看到它的 Objective-C 軟肋。

堅(jiān)持下去!正如 Maya Angelou 所說,“所有偉大的成就都需要時(shí)間”——理解 Core Data 在這里為我們所做的一切需要一些工作,但它會(huì)得到回報(bào),我相信你會(huì)喜歡在你的應(yīng)用程序。

今天你要完成三個(gè)主題,在這些主題中你將了解NSPredicate、動(dòng)態(tài)更改提取請(qǐng)求、創(chuàng)建關(guān)系等。

有一次你會(huì)看到我說你已經(jīng)達(dá)到了一個(gè)很好的點(diǎn),可以繼續(xù)學(xué)習(xí)下一個(gè)教程,但如果你繼續(xù)超越那個(gè)點(diǎn),我們將探索一些更高級(jí)的主題。需要明確的是,額外的工作是可選的:如果你時(shí)間緊迫,或者只是想打下基礎(chǔ),請(qǐng)不要這樣做。

使用 NSPredicate 過濾 @FetchRequest

當(dāng)我們使用 SwiftUI 的@FetchRequest屬性包裝器時(shí),我們可以提供一個(gè)排序描述符數(shù)組來控制結(jié)果的排序,但我們也可以提供一個(gè)NSPredicate來控制應(yīng)該顯示哪些結(jié)果。謂詞是簡(jiǎn)單的測(cè)試,測(cè)試將應(yīng)用于我們Core Data實(shí)體中的每個(gè)對(duì)象——只有通過測(cè)試的對(duì)象才會(huì)包含在結(jié)果數(shù)組中。

NSPredicate的語法不是你可以輕易猜到的,但實(shí)際上你只需要幾種類型的謂詞,所以它并不像你想象的那么糟糕。

要嘗試一些謂詞,請(qǐng)創(chuàng)建一個(gè)名為 Ship 的新實(shí)體,它具有兩個(gè)字符串屬性:“name”和“universe”。

現(xiàn)在將 ContentView.swift 修改為:

我們現(xiàn)在可以按下按鈕將一些樣本數(shù)據(jù)注入 Core Data,但現(xiàn)在我們沒有謂詞。為了解決這個(gè)問題,我們需要將nil謂詞值替換為可以應(yīng)用于我們的對(duì)象的某種測(cè)試。

例如,我們可以像這樣請(qǐng)求星球大戰(zhàn)中的飛船:

如果你的數(shù)據(jù)包含引號(hào),這會(huì)變得很復(fù)雜,因此使用特殊語法更常見:`%@' 表示“在此處插入一些數(shù)據(jù)”,并允許我們將該數(shù)據(jù)作為參數(shù)提供給謂詞而不是內(nèi)聯(lián)。

所以,我們可以這樣寫:

除了==,我們還可以使用諸如<>之類的比較來過濾我們的對(duì)象。例如,這將返回 Defiant、Enterprise 和 Executor:

%@在幕后做了很多工作,特別是在將原生 Swift 類型轉(zhuǎn)換為 Core Data 等價(jià)物時(shí)。例如,我們可以使用IN謂詞來檢查 universe 是否是數(shù)組中的三個(gè)選項(xiàng)之一,如下所示:

我們還可以使用謂詞來檢查字符串的一部分,使用諸如BEGINSWITHCONTAINS之類的運(yùn)算符。例如,這將返回所有以大寫字母 E 開頭的船只:

該謂詞區(qū)分大小寫;如果你想忽略大小寫,則需要將其修改為:

CONTAINS[c]工作方式類似,除了不是以你的子字符串開頭,它可以在屬性內(nèi)的任何位置。

最后,你可以翻轉(zhuǎn)謂詞使用NOT, 以獲得它們常規(guī)行為的反轉(zhuǎn)。例如,這會(huì)找到所有不以 E 開頭的船只:

如果你需要更復(fù)雜的謂詞,將它們加入使用AND以根據(jù)需要建立盡可能多的精度,或者添加一個(gè) Core Data 導(dǎo)入并查看NSCompoundPredicate- 它可以讓你從幾個(gè)較小的謂詞中構(gòu)建一個(gè)謂詞。

使用 SwiftUI 動(dòng)態(tài)過濾@FetchRequest

我被問到最多的 SwiftUI 問題之一是:如何動(dòng)態(tài)更改核心數(shù)據(jù)@FetchRequest以使用不同的謂詞或排序順序?出現(xiàn)這個(gè)問題是因?yàn)楂@取請(qǐng)求是作為一個(gè)屬性創(chuàng)建的,所以如果你試圖讓它們引用另一個(gè)屬性,Swift 將拒絕。

這里有一個(gè)簡(jiǎn)單的解決方案,回想起來通常很明顯,因?yàn)樗瞧渌磺械墓ぷ鞣绞剑何覀儜?yīng)該將我們想要的功能分割成一個(gè)單獨(dú)的視圖,然后將值注入其中。

我想用一些真實(shí)的代碼來演示這一點(diǎn),所以我把最簡(jiǎn)單的例子放在一起:它向 Core Data 添加了三位歌手,然后使用兩個(gè)按鈕來顯示姓氏以 A 或 S 結(jié)尾的歌手。

首先創(chuàng)建一個(gè)名為 Singer 的新核心數(shù)據(jù)實(shí)體,并為其提供兩個(gè)字符串屬性:“firstName”和“l(fā)astName”。使用數(shù)據(jù)模型檢查器將其 Codegen 更改為 Manual/None,然后轉(zhuǎn)到 Editor 菜單并選擇 Create NSManagedObject Subclass 這樣我們就可以獲得一個(gè)Singer我們可以自定義的類。

Xcode 為我們生成文件后,打開 Singer+CoreDataProperties.swift 并添加這兩個(gè)屬性,使該類更易于與 SwiftUI 一起使用:

好的,現(xiàn)在進(jìn)入真正的工作。

第一步是設(shè)計(jì)一個(gè)視圖來承載我們的信息。就像我說的,這也將有兩個(gè)按鈕,讓我們改變視圖的過濾方式,我們將有一個(gè)額外的按鈕來插入一些測(cè)試數(shù)據(jù),這樣你就可以看到它是如何工作的。

首先,向你的ContentView結(jié)構(gòu)體添加兩個(gè)屬性,以便我們有一個(gè)可以將對(duì)象保存到的托管對(duì)象上下文,以及一些我們可以用作過濾器的狀態(tài):

對(duì)于視圖的主體,我們將使用VStack帶有三個(gè)按鈕的 ,以及我們希望List顯示匹配歌手的位置的注釋:

到目前為止,很容易?,F(xiàn)在是有趣的部分:我們需要用真實(shí)的東西替換那個(gè)評(píng)論// list of matching singers。這不會(huì)被使用@FetchRequest,因?yàn)槲覀兿M軌蛟诔跏蓟绦蛑袆?chuàng)建自定義獲取請(qǐng)求,但我們將使用的代碼幾乎是相同的。

創(chuàng)建一個(gè)名為“FilteredList”的新 SwiftUI 視圖,并為其賦予以下屬性:

這將存儲(chǔ)我們的獲取請(qǐng)求,以便我們可以在body中循環(huán)遍歷它.?然而,我們不在這里創(chuàng)建獲取請(qǐng)求,因?yàn)槲覀內(nèi)匀徊恢牢覀円阉魇裁础O喾?,我們將?chuàng)建一個(gè)自定義初始化器,它接受一個(gè)過濾器字符串并使用它來設(shè)置fetchRequest屬性。

現(xiàn)在添加這個(gè)初始化器:

這將使用當(dāng)前托管對(duì)象上下文運(yùn)行獲取請(qǐng)求。因?yàn)檫@個(gè)視圖將在內(nèi)部使用ContentView,所以我們甚至不需要將托管對(duì)象上下文注入到環(huán)境中——它會(huì)從ContentView中繼承上下文。

你是否注意到開頭有一個(gè)下劃線_fetchRequest?那是故意的。你看,我們不是在獲取請(qǐng)求中寫入獲取的結(jié)果對(duì)象,而是編寫一個(gè)全新的獲取請(qǐng)求。

要理解這一點(diǎn),請(qǐng)考慮@State屬性包裝器。在這個(gè)場(chǎng)景的背后,它被實(shí)現(xiàn)為一個(gè)名為 State的結(jié)構(gòu),它包含我們放入其中的任何值——例如,一個(gè)整數(shù)。如果我們有一個(gè)@State屬性叫做score并將值 10 賦給它,我們的意思是將 10 放入State屬性包裝器內(nèi)的整數(shù)中。然而,我們也可以給它賦值_score——如果需要的話,我們可以在里面寫一個(gè)全新的State結(jié)構(gòu)。

因此,通過分配給_fetchRequest,我們并不是要說“這里有一大堆我們希望你使用的新結(jié)果”,而是告訴 Swift 我們想要更改整個(gè)獲取請(qǐng)求本身。

剩下的就是寫視圖的主體,所以給視圖這個(gè)主體:

至于FilteredList的預(yù)覽結(jié)構(gòu),你可以安全地將其刪除。

現(xiàn)在視圖已經(jīng)完成,我們可以返回ContentView并用一些將我們的過濾器傳遞到的實(shí)際代碼替換注釋FilteredList

現(xiàn)在運(yùn)行程序試一試:首先點(diǎn)擊“添加示例”按鈕創(chuàng)建三個(gè)歌手對(duì)象,然后點(diǎn)擊“顯示 A”或“顯示 S”以在姓氏字母之間切換。你應(yīng)該看到我們List使用不同的數(shù)據(jù)動(dòng)態(tài)更新,具體取決于你按下的按鈕。

因此,需要一些新知識(shí)才能完成這項(xiàng)工作,但實(shí)際上并沒有那么難——只要你像 SwiftUI 一樣思考,解決方案就在那里。

提示:你可能會(huì)查看我們的代碼并認(rèn)為每次重新創(chuàng)建視圖時(shí)——即每次容器視圖中的任何狀態(tài)更改時(shí)——我們也在重新創(chuàng)建獲取請(qǐng)求,這反過來意味著在沒有其他情況時(shí)從數(shù)據(jù)庫中讀取已經(jīng)改變。

這可能看起來非常浪費(fèi),如果它真的發(fā)生了,那將是非常浪費(fèi)。幸運(yùn)的是,Core Data 不會(huì)做這樣的事情:它只會(huì)在過濾器字符串更改時(shí)重新運(yùn)行數(shù)據(jù)庫查詢,即使重新創(chuàng)建視圖也是如此。

想更進(jìn)一步?

為了獲得更大的靈活性,我們可以改進(jìn)我們的FilteredList視圖,使其適用于任何類型的實(shí)體,并且可以在任何字段上進(jìn)行過濾。為了使其正常工作,我們需要進(jìn)行一些更改:

  1. 我們將使用泛型,而不是專門引用Singer該類,但有一個(gè)約束,即傳入的任何內(nèi)容都必須是一個(gè)NSManagedObject.

  2. 我們需要接受第二個(gè)參數(shù)來決定我們要過濾的鍵名,因?yàn)槲覀兛赡苷谑褂脹]有lastName屬性的實(shí)體。

  3. 因?yàn)槲覀兪孪炔恢烂總€(gè)實(shí)體將包含什么,所以我們將讓我們的包含視圖來決定。因此,我們不只是使用歌手名字的文本視圖,而是要求一個(gè)可以運(yùn)行的閉包來配置他們想要的視圖。

里面有兩個(gè)復(fù)雜的部分。第一個(gè)是決定每個(gè)列表行的內(nèi)容的閉包,因?yàn)樗枰褂脙蓚€(gè)重要的語法。我們?cè)谠缙陉P(guān)于視圖和修飾符的技術(shù)項(xiàng)目即將結(jié)束時(shí)查看了這些內(nèi)容,但如果你錯(cuò)過了它們:

  • 如果需要,@ViewBuilder讓我們的包含視圖(無論使用列表的是什么)發(fā)送多個(gè)視圖。

  • @escaping表示閉包將被存儲(chǔ)起來并在以后使用,這意味著 Swift 需要照顧好它的內(nèi)存。

第二個(gè)復(fù)雜的部分是我們?nèi)绾巫屓萜饕晥D自定義搜索鍵。以前我們是這樣控制過濾值的:

因此,你可能會(huì)進(jìn)行有根據(jù)的猜測(cè)并編寫如下代碼:

但是,那是行不通的。你看,當(dāng)我們編寫%@?Core Data 時(shí),會(huì)自動(dòng)為我們插入引號(hào),以便謂詞正確讀取。這很有用,因?yàn)槿绻覀兊淖址?hào),它會(huì)自動(dòng)確保它們不會(huì)與它添加的引號(hào)沖突。

這意味著當(dāng)我們使用%@屬性名稱時(shí),我們可能會(huì)得到這樣的謂詞:

這是不正確的:屬性名稱不應(yīng)包含在引號(hào)中。

為了解決這個(gè)問題,NSPredicate有一個(gè)特殊的符號(hào)可以用來替換屬性名稱:%K,代表“鍵”。這將插入我們提供的值,但不會(huì)在它們周圍添加引號(hào)。正確的謂詞是這樣的:

所以,為 CoreData 添加一個(gè)導(dǎo)入,以便我們可以引用NSManagedObject,然后用這個(gè)替換你當(dāng)前的FilteredList結(jié)構(gòu):

我們現(xiàn)在可以通過ContentView像這樣升級(jí)來使用新的過濾列表:

請(qǐng)注意我是如何專門用作(singer: Singer)閉包參數(shù)的——這是必需的,以便 Swift 了解FilteredList其使用方式。請(qǐng)記住,我們說過它可以是任何類型的NSManagedObject,但為了讓 Swift 準(zhǔn)確知道它是什么類型的托管對(duì)象,我們需要明確說明。

不管怎樣,有了這個(gè)改變,我們現(xiàn)在可以將我們的列表與任何類型的過濾器鍵和任何類型的實(shí)體一起使用——它更有用了!

與 Core Data、SwiftUI 和 @FetchRequest 的一對(duì)多關(guān)系

Core Data 允許我們使用關(guān)系將實(shí)體鏈接在一起,當(dāng)我們使用@FetchRequest?Core Data 時(shí),會(huì)將所有數(shù)據(jù)發(fā)送回給我們使用。然而,這是 Core Data 略顯陳舊的一個(gè)領(lǐng)域:為了讓關(guān)系運(yùn)作良好,我們需要?jiǎng)?chuàng)建一個(gè)自定義NSManagedObject子類,提供對(duì) SwiftUI 更友好的包裝器。

為了證明這一點(diǎn),我們將構(gòu)建兩個(gè) Core Data 實(shí)體:一個(gè)用于跟蹤糖果棒,另一個(gè)用于跟蹤這些棒棒糖的來源國。

關(guān)系有四種形式:

  • 一對(duì)一關(guān)系意味著一個(gè)實(shí)體中的一個(gè)對(duì)象恰好鏈接到另一個(gè)實(shí)體中的一個(gè)對(duì)象。在我們的例子中,這意味著每種糖果都有一個(gè)原產(chǎn)國,每個(gè)國家只能生產(chǎn)一種糖果。

  • 一對(duì)多關(guān)系意味著實(shí)體中的一個(gè)對(duì)象鏈接到另一個(gè)實(shí)體中的許多對(duì)象。在我們的例子中,這意味著一種糖果可以同時(shí)在許多國家推出,但每個(gè)國家仍然只能生產(chǎn)一種糖果。

  • 多對(duì)一關(guān)系意味著一個(gè)實(shí)體中的許多對(duì)象鏈接到另一個(gè)實(shí)體中的一個(gè)對(duì)象。在我們的示例中,這意味著每種糖果都有一個(gè)原產(chǎn)國,并且每個(gè)國家可以生產(chǎn)多種糖果。

  • 多對(duì)多關(guān)系意味著一個(gè)實(shí)體中的許多對(duì)象鏈接到另一個(gè)實(shí)體中的許多對(duì)象。在我們的示例中,這意味著許多國家同時(shí)引入了一種糖果,并且每個(gè)國家都可以生產(chǎn)多種糖果。

所有這些都在不同的時(shí)間使用,但在我們的糖果示例中,多對(duì)一關(guān)系最有意義——每種糖果都是在一個(gè)國家發(fā)明的,但每個(gè)國家可以發(fā)明多種糖果。

因此,打開你的數(shù)據(jù)模型并添加兩個(gè)實(shí)體:Candy,帶有名為“name”的字符串屬性,以及 Country,帶有名為“fullName”和“shortName”的字符串屬性。盡管某些類型的糖果具有相同的名稱(如美國和英國的“Smarties”),但國家/地區(qū)絕對(duì)是獨(dú)一無二的,因此請(qǐng)為“shortName”添加一個(gè)約束條件。

提示:如果你忘記了如何添加約束,請(qǐng)不要擔(dān)心:選擇 Country 實(shí)體,轉(zhuǎn)到 View 菜單并選擇 Inspectors > Data Model,單擊 Constraints 下的 + 按鈕,然后將示例重命名為“shortName”。

在完成此數(shù)據(jù)模型之前,我們需要告訴 Core Data Candy 和 Country 之間存在一對(duì)多關(guān)系:

  • 選擇國家/地區(qū)后,在關(guān)系表下按 +。調(diào)用關(guān)系“candy”,將其目的地更改為 Candy,然后在數(shù)據(jù)模型檢查器中將 Type 更改為 To Many。

  • 現(xiàn)在選擇 Candy,并在那里添加另一個(gè)關(guān)系。將關(guān)系稱為“來源”,將其目的地更改為“國家/地區(qū)”,然后將其倒數(shù)設(shè)置為“糖果”,以便 Core Data 理解鏈接是雙向的。

這就完成了我們的實(shí)體,下一步是查看 Xcode 為我們生成的代碼。請(qǐng)記住按 Cmd+S 強(qiáng)制 Xcode 保存你的更改。

選擇 Candy 和 Country 并將它們的 Codegen 設(shè)置為 Manual/None,然后轉(zhuǎn)到 Editor 菜單并選擇 Create NSManagedObject Subclass 為我們的兩個(gè)實(shí)體創(chuàng)建代碼——記住將它們保存在 CoreDataProject 組和文件夾中。

當(dāng)我們選擇兩個(gè)實(shí)體時(shí),Xcode 將為我們生成四個(gè) Swift 文件。Candy+CoreDataProperties.swift 幾乎完全符合你的期望,盡管請(qǐng)注意origin現(xiàn)在是一個(gè)Country.?Country+CoreDataProperties.swift比較復(fù)雜,因?yàn)閄code也生成了一些方法供我們使用。

之前我們研究了如何使用子類清理 Core Data 的可選值NSManagedObject,但這里有一個(gè)額外的復(fù)雜性:Country類有一個(gè)candy屬性是NSSet.?這是舊的 Objective-C 數(shù)據(jù)類型,相當(dāng)于 Swift 的Set,但我們不能將它與 SwiftUI 一起使用ForEach

為了解決這個(gè)問題,我們需要修改 Xcode 為我們生成的文件,添加使 SwiftUI 運(yùn)行良好的便利包裝器。對(duì)于這個(gè)Candy類來說,這就像包裝name屬性一樣簡(jiǎn)單,以便它始終返回一個(gè)字符串:

對(duì)于這個(gè)Country類,我們可以在shortNamefullName周圍創(chuàng)建相同的字符串包裝器,如下所示:

然而,當(dāng)涉及到candy時(shí),事情就復(fù)雜多了。這是一個(gè)NSSet,它可以包含任何東西,因?yàn)?Core Data 沒有將它限制為Candy.

所以,為了讓這個(gè)東西變成對(duì) SwiftUI 有用的形式,我們需要:

  1. 將它從一個(gè)NSSet轉(zhuǎn)換Set<Candy>——我們知道其內(nèi)容類型的 Swift 原生類型。

  2. 將其Set<Candy>轉(zhuǎn)換為數(shù)組,以便ForEach可以從那里讀取單個(gè)值。

  3. 對(duì)該數(shù)組進(jìn)行排序,使糖果條以合理的順序排列。

Swift 實(shí)際上讓我們同時(shí)執(zhí)行步驟 2 和 3,因?yàn)閷?duì)集合進(jìn)行排序會(huì)自動(dòng)返回一個(gè)數(shù)組。然而,對(duì)數(shù)組進(jìn)行排序比你想象的要難:這是一個(gè)自定義類型的數(shù)組,所以我們不能只使用sorted()并讓 Swift 弄清楚它。相反,我們需要提供一個(gè)閉包來接受兩個(gè)糖果塊,如果第一塊糖果應(yīng)該排在第二塊之前,則返回 true。

因此,現(xiàn)在請(qǐng)將此計(jì)算屬性添加到Country

這就完成了我們的Core Data類,所以現(xiàn)在我們可以編寫一些 SwiftUI 代碼來完成所有這些工作。

打開 ContentView.swift 并賦予它以下兩個(gè)屬性:

請(qǐng)注意我們不需要在我們的獲取請(qǐng)求中指定任何關(guān)于關(guān)系的信息——Core Data 理解實(shí)體是鏈接的,所以它會(huì)根據(jù)需要獲取它們。

至于視圖的主體,我們將使用一個(gè)List包含兩個(gè)ForEach視圖的 :一個(gè)為每個(gè)國家/地區(qū)創(chuàng)建一個(gè)部分,另一個(gè)為每個(gè)國家/地區(qū)創(chuàng)建糖果。這個(gè)List將依次進(jìn)入 VStack因此我們可以在下面添加一個(gè)按鈕來生成一些示例數(shù)據(jù):

請(qǐng)務(wù)必運(yùn)行該代碼,因?yàn)樗\(yùn)行良好——當(dāng)點(diǎn)擊“添加”按鈕時(shí),我們所有的糖果條都會(huì)自動(dòng)分類。更好的是,因?yàn)槲覀冊(cè)?strong>NSManagedObject子類中完成了所有繁重的工作,生成的 SwiftUI 代碼實(shí)際上非常簡(jiǎn)單——它不知道幕后有一個(gè)NSSet,因此更容易理解。

提示:如果你在按添加后沒有看到你的糖果條被分類到不同的部分,請(qǐng)確保你沒有從類DataController中刪除mergePolicy更改。提醒一下,它應(yīng)該設(shè)置為NSMergePolicy.mergeByPropertyObjectTrump.




SwiftUI學(xué)習(xí)100天(Day58 - 項(xiàng)目 12,第二部分)的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
武功县| 原平市| 扎鲁特旗| 化德县| 滦平县| 渝北区| 昌乐县| 神农架林区| 若尔盖县| 江城| 吉木乃县| 乐业县| 张家口市| 清涧县| 乌鲁木齐市| 陇西县| 呼玛县| 获嘉县| 博爱县| 饶平县| 平阳县| 会昌县| 淮滨县| 喜德县| 白城市| 乳山市| 古田县| 蕉岭县| 寿宁县| 平利县| 津市市| 和林格尔县| 衡山县| 宁远县| 赤城县| 陆丰市| 宁陵县| 桐梓县| 山东| 泰来县| 客服|