五月天青色头像情侣网名,国产亚洲av片在线观看18女人,黑人巨茎大战俄罗斯美女,扒下她的小内裤打屁股

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

SwiftUI學(xué)習(xí)100天(Day90 - 項(xiàng)目 17,第五部分)

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

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

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

今天我們通過添加一些最終功能并修復(fù)大量錯(cuò)誤來(lái)結(jié)束我們的程序。是的,我們的程序有錯(cuò)誤,我將向你介紹其中的一些錯(cuò)誤,并向你展示如何修復(fù)它們。

當(dāng)你學(xué)習(xí)編程時(shí),發(fā)現(xiàn)代碼中的錯(cuò)誤可能會(huì)令人沮喪,因?yàn)楦杏X就像你搞砸了一樣。但正如傳奇的荷蘭計(jì)算機(jī)科學(xué)家 Edsger Dijkstra 曾經(jīng)說(shuō)過的那樣,“如果調(diào)試是消除錯(cuò)誤的過程,那么編程一定是將它們放入的過程?!?/p>

換句話說(shuō),當(dāng)你在開發(fā)軟件時(shí),修復(fù)錯(cuò)誤是理所當(dāng)然的,因?yàn)槲覀儾⒉煌昝?。你?duì)創(chuàng)建錯(cuò)誤、查找錯(cuò)誤和修復(fù)錯(cuò)誤越自在,你就會(huì)成為更好的開發(fā)人員。

今天你要完成三個(gè)主題,其中你將添加觸覺反饋,修復(fù)我們應(yīng)用程序中的許多錯(cuò)誤,然后添加一個(gè)新屏幕來(lái)編輯卡片。


使用 UINotificationFeedbackGenerator 讓 iPhone 振動(dòng)

iOS 帶有許多用于生成觸覺反饋的選項(xiàng),它們都可供我們?cè)?SwiftUI 中使用。在最簡(jiǎn)單的形式中,這就像創(chuàng)建UIFeedbackGenerator其中一個(gè)子類的實(shí)例然后在你準(zhǔn)備好時(shí)觸發(fā)它一樣簡(jiǎn)單,但是為了更精確地控制反饋,你應(yīng)該首先調(diào)用它的prepare()方法讓 Taptic Engine 有機(jī)會(huì)預(yù)熱.

重要提示:預(yù)熱 Taptic Engine 有助于減少我們播放效果和實(shí)際發(fā)生之間的延遲,但它也會(huì)對(duì)電池產(chǎn)生影響,因此系統(tǒng)只會(huì)在你調(diào)用prepare()后準(zhǔn)備一兩秒鐘。

我們可以使用幾個(gè)不同的UIFeedbackGenerator子類,但我們?cè)谶@里要使用的是UINotificationFeedbackGenerator,因?yàn)樗峁┝嗽?iOS 中常見的成功和失敗觸覺。現(xiàn)在,我們可以向ContentView添加一個(gè)UINotificationFeedbackGenerator的中心實(shí)例,但這會(huì)導(dǎo)致一個(gè)問題:ContentView每當(dāng)一張卡片被移除時(shí)都會(huì)收到通知,但在拖動(dòng)過程中不會(huì)收到通知,這意味著我們沒有機(jī)會(huì)預(yù)熱啟動(dòng) Taptic Engine。

因此,我們將為每個(gè)CardView提供自己的UINotificationFeedbackGenerator實(shí)例,以便他們可以根據(jù)需要準(zhǔn)備和播放它們。系統(tǒng)將負(fù)責(zé)確保觸覺都整齊排列,因此它們不會(huì)以某種方式混淆。

將此新屬性添加到CardView

現(xiàn)在找到CardView拖動(dòng)手勢(shì)中的removal?(),并將整個(gè)閉包更改為:

僅此一項(xiàng)就足以在我們的應(yīng)用程序中獲得觸覺,但始終存在觸覺延遲的風(fēng)險(xiǎn),因?yàn)?Taptic Engine 尚未準(zhǔn)備就緒。在這種情況下,觸覺仍會(huì)播放,但可能會(huì)延遲半秒——足以讓人感覺與我們的用戶界面有一點(diǎn)點(diǎn)脫節(jié)。

為了改善這一點(diǎn),我們需要在觸發(fā)觸覺之前稍微調(diào)用一下prepare()在激活之前立即調(diào)用prepare()是不夠的:這樣做不會(huì)給Taptic?Engine足夠的預(yù)熱時(shí)間,因此你不會(huì)看到延遲有任何減少。相反,你應(yīng)該在知道可能需要觸覺時(shí)立即調(diào)用prepare()。

現(xiàn)在,你應(yīng)該了解兩個(gè)有用的實(shí)施細(xì)節(jié)。

首先,可以調(diào)用prepare()然后永遠(yuǎn)不會(huì)觸發(fā)效果 - 系統(tǒng)將使 Taptic Engine 準(zhǔn)備好幾秒鐘,然后再次將其關(guān)閉。如果你反復(fù)調(diào)用prepare()并且從未觸發(fā)它,系統(tǒng)可能會(huì)開始忽略你的prepare()調(diào)用,直到至少發(fā)生一種效果。

其次,完全允許在觸發(fā)一次之前調(diào)用prepare()多次——?prepare()在 Taptic Engine 預(yù)熱時(shí)不會(huì)暫停你的應(yīng)用程序,并且在系統(tǒng)已經(jīng)準(zhǔn)備好時(shí)也不會(huì)產(chǎn)生任何實(shí)際性能成本。

將這兩個(gè)放在一起,我們將更新我們的拖動(dòng)手勢(shì),以便在手勢(shì)發(fā)生變化時(shí)調(diào)用prepare()。這意味著在最終觸發(fā)觸覺之前它可能會(huì)被調(diào)用一百次,因?yàn)槊看斡脩粢苿?dòng)手指時(shí)它都會(huì)被觸發(fā)。

因此,將你的onChanged()閉包修改為:

現(xiàn)在繼續(xù)嘗試該應(yīng)用程序,看看你的想法 - 根據(jù)你滑動(dòng)的方向,你應(yīng)該能夠感受到兩種截然不同的觸覺。

在我們結(jié)束觸覺之前,我希望你考慮一件事。多年前,百事公司向商場(chǎng)購(gòu)物者發(fā)起“百事可樂挑戰(zhàn)賽”:先喝一口可樂,再喝一口另一種,看看你更喜歡哪一種。結(jié)果發(fā)現(xiàn),與可口可樂相比,更多美國(guó)人更喜歡百事可樂,盡管可口可樂的市場(chǎng)份額要大得多。然而,有一個(gè)問題:人們似乎在測(cè)試中選擇了百事可樂,因?yàn)榘偈驴蓸返奈兜栏?,雖然它在小口量中效果很好,但在罐裝和瓶裝中效果不佳,而人們實(shí)際上更喜歡可口可樂。

我這么說(shuō)的原因是因?yàn)槲覀冊(cè)谖覀兊膽?yīng)用程序中添加了兩個(gè)觸覺通知,這些通知會(huì)經(jīng)常播放。當(dāng)你進(jìn)行小劑量測(cè)試時(shí),這些觸覺可能感覺很棒 – 你讓手機(jī)嗡嗡作響,這真的很令人愉快。但是,如果你是這個(gè)應(yīng)用程序的忠實(shí)用戶,那么我們的觸覺可能會(huì)遇到兩個(gè)問題:

  1. 用戶可能會(huì)覺得它們很煩人,因?yàn)樗鼈兠績(jī)傻饺刖蜁?huì)發(fā)生一次,具體取決于它們的速度。

  2. 更糟糕的是,用戶可能對(duì)它們變得不敏感——它們失去了所有有用性,無(wú)論是作為通知還是作為一點(diǎn)點(diǎn)喜悅的火花。

所以,既然你已經(jīng)親自嘗試過了,我希望你考慮一下應(yīng)該如何使用它們。如果這是我的應(yīng)用程序,我可能會(huì)保留失敗的觸覺,但我認(rèn)為成功的觸覺可能會(huì)消失——那個(gè)可能最常被觸發(fā),這意味著當(dāng)失敗的觸覺播放時(shí)感覺會(huì)更特別一些。

修復(fù)錯(cuò)誤

到目前為止,我們的 SwiftUI 應(yīng)用程序看起來(lái)不錯(cuò):我們有一堆可以拖動(dòng)來(lái)控制應(yīng)用程序的卡片,還有觸覺反饋和一些輔助功能支持。但與此同時(shí),它也充滿了阻礙它發(fā)展的故障——有些大,有些小,但都值得解決。

首先,可以在卡片不在頂部時(shí)四處拖動(dòng)卡片。這讓用戶感到困惑,因?yàn)樗麄兛梢宰ト∫粡埶麄儗?shí)際上看不到的卡片,所以這需要永遠(yuǎn)不可能。

為了解決這個(gè)問題,我們將使用allowsHitTesting()這樣只有最后一張卡片——最上面的那張——可以被拖來(lái)拖去。找到在ContentView中的stacked()修改器并直接在下面添加:

其次,我們的 UI 在與 VoiceOver 一起使用時(shí)有點(diǎn)亂。如果你在啟用了 VoiceOver 的真實(shí)設(shè)備上啟動(dòng)它,你會(huì)發(fā)現(xiàn)你可以點(diǎn)擊背景圖像來(lái)讀出“背景,圖像”,這是毫無(wú)意義的。然而,事情變得更糟了:向右輕掃一下,VoiceOver 就會(huì)在所有輔助元素之間移動(dòng)——它會(huì)讀出我們所有卡片中的文本,即使是那些不可見的卡片。

要解決背景圖像問題,我們應(yīng)該讓它使用裝飾圖像,這樣它就不會(huì)作為輔助功能布局的一部分被讀出。將背景圖片修改為:

要修復(fù)卡片,我們需要使用與我們一分鐘前添加的accessibilityHidden()修改器具有相似條件的修改器allowsHitTesting()。在這種情況下,索引小于頂部卡片的每張卡片都應(yīng)該從可訪問性系統(tǒng)中隱藏,因?yàn)樗鼘?duì)卡片沒有任何用處,因此將其直接添加到allowsHitTesting()修飾符下方

我們的應(yīng)用程序存在第三個(gè)可訪問性問題,這是使用手勢(shì)控制事物的直接結(jié)果。是的,大多數(shù)時(shí)候使用手勢(shì)非常有趣,但如果你有特定的輔助功能需求,則可能很難使用它們。

在此應(yīng)用程序中,我們的手勢(shì)導(dǎo)致了多個(gè)問題:VoiceOver 用戶不清楚他們應(yīng)該如何控制該應(yīng)用程序:

  1. 我們不會(huì)說(shuō)卡片是可以點(diǎn)擊的按鈕。

  2. 當(dāng)答案被揭示時(shí),沒有聲音通知它是什么。

  3. 用戶無(wú)法通過向左或向右滑動(dòng)來(lái)瀏覽卡片。

解決這些問題只需要很少的工作,但回報(bào)是我們的應(yīng)用程序更容易為每個(gè)人所用。

首先,我們需要明確我們的卡片是可點(diǎn)擊的按鈕。這就像在.isButton中添加accessibilityAddTraits()CardViewZStack一樣簡(jiǎn)單。把這個(gè)放在它的opacity()修飾符之后:

現(xiàn)在系統(tǒng)將顯示“誰(shuí)在神秘博士中扮演第 13 位醫(yī)生?按鈕”——向用戶提示卡片可以被點(diǎn)擊的重要提示。

其次,我們需要幫助系統(tǒng)讀取卡片的答案以及問題?,F(xiàn)在這是可能的,但前提是用戶在屏幕上四處滑動(dòng)——這遠(yuǎn)非顯而易見。因此,為了解決這個(gè)問題,我們將檢測(cè)用戶是否在他們的設(shè)備上啟用了輔助功能,如果啟用,則在顯示提示和顯示答案之間自動(dòng)切換。也就是說(shuō),我們不會(huì)將答案顯示在提示下方,而是將其關(guān)閉并只顯示答案,這將使 VoiceOver 立即讀出它。

SwiftUI 提供了一個(gè)特定的環(huán)境屬性來(lái)告訴我們 VoiceOver 何時(shí)運(yùn)行,稱為accessibilityVoiceOverEnabled.?因此,將此新屬性添加到CardView

現(xiàn)在我們顯示提示和答案的代碼如下所示:

我們將對(duì)其進(jìn)行更改,以便將提示和答案顯示在單個(gè)文本視圖中,并由accessibilityEnabled決定顯示哪種布局。將你的代碼修改為:

如果你使用 VoiceOver 嘗試一下,你會(huì)發(fā)現(xiàn)它的效果要好得多——只要雙擊名片,答案就會(huì)被讀出。

第三,我們需要讓用戶更容易將卡片標(biāo)記為正確或錯(cuò)誤,因?yàn)楝F(xiàn)在我們的圖像還不能切割它。它們不僅會(huì)阻止用戶使用點(diǎn)擊手勢(shì)與我們的應(yīng)用程序進(jìn)行交互,還會(huì)被讀作他們的 SF Symbols 名稱——“復(fù)選標(biāo)記、圓圈、圖像”——而不是任何有用的東西。

為了解決這個(gè)問題,我們需要用實(shí)際移除卡片的按鈕替換圖像。如果用戶是正確的或錯(cuò)誤的,我們實(shí)際上并沒有做任何不同的事情——我需要為你的挑戰(zhàn)留下一些東西!– 但我們至少可以從牌組中取出最上面的牌。同時(shí),我們將提供可訪問性標(biāo)簽和提示,以便用戶更好地了解按鈕的功能。

所以,用這個(gè)新代碼用這些圖像替換你當(dāng)前的HStack

因?yàn)榧词棺詈笠粡埧ㄆ驯灰瞥?,這些按鈕仍保留在屏幕上,所以我們需要removeCard(at:)?的開頭添加一個(gè)guard檢查以確保我們不會(huì)嘗試移除不存在的卡片。因此,將這一行新代碼放在該方法的開頭:

最后,我們可以在啟用differentiateWithoutColor或啟用 VoiceOver 時(shí)使這些按鈕可見。這意味著將另一個(gè)accessibilityVoiceOverEnabled屬性添加到ContentView

然后將if differentiateWithoutColor {條件修改為:

通過這些可訪問性更改,我們的應(yīng)用程序?qū)γ總€(gè)人都更好地工作——干得好!

在我們完成之前,我想添加一個(gè)小的額外更改。現(xiàn)在,如果你稍微拖動(dòng)一個(gè)圖像然后放手,我們將它的偏移量設(shè)置回零,這會(huì)導(dǎo)致它跳回到屏幕的中心。如果我們將彈簧動(dòng)畫附加到我們的卡片上,它將滑入中心,我認(rèn)為這可以更清楚地向我們的用戶指示實(shí)際發(fā)生的事情。

要做到這一點(diǎn),請(qǐng)?jiān)?span id="5tt3ttt3t" class="color-pink-02">CardViewZStack末尾添加一個(gè)animation()修飾符緊跟在onTapGesture()后面:

好多了!

提示:如果仔細(xì)觀察,你可能會(huì)注意到,如果將卡片向右拖動(dòng)一點(diǎn)然后松開,卡片會(huì)閃爍紅色。稍后會(huì)詳細(xì)介紹!


添加和刪除卡片

到目前為止,我們所做的一切都使用了一組固定的示例卡片,但當(dāng)然,只有當(dāng)用戶可以真正自定義他們看到的卡片列表時(shí),這個(gè)應(yīng)用程序才有用。這意味著添加一個(gè)列出所有現(xiàn)有卡片的新視圖,并允許用戶添加一個(gè)新的視圖,這是你以前見過的所有內(nèi)容。然而,這次有一個(gè)有趣的問題需要一些新的東西來(lái)修復(fù),所以值得解決這個(gè)問題。

首先我們需要一些狀態(tài)來(lái)控制我們的編輯屏幕是否可見。因此,將其添加到ContentView

接下來(lái)我們需要添加一個(gè)按鈕以在點(diǎn)擊時(shí)翻轉(zhuǎn)該布爾值,因此找到if differentiateWithoutColor || accessibilityEnabled條件并將其放在它之前:

我們將設(shè)計(jì)一個(gè)新EditCards視圖,來(lái)將Card數(shù)組編碼和解碼UserDefaults,但在我們這樣做之前,我希望你使Card結(jié)構(gòu)符合Codable如下所示:

現(xiàn)在創(chuàng)建一個(gè)名為“EditCards”的新 SwiftUI 視圖。這需要:

  1. 有自己的Card數(shù)組。

  2. 包裹在NavigationView,這樣我們就可以添加一個(gè)完成按鈕來(lái)關(guān)閉視圖。

  3. 有一個(gè)顯示所有現(xiàn)有卡片的列表。

  4. 添加滑動(dòng)以刪除這些卡片。

  5. 在列表頂部有一個(gè)部分,以便用戶可以添加新卡。

  6. 具有從UserDefaults.

我們之前已經(jīng)看過所有這些代碼,所以我不打算在這里再次解釋。我希望你能停下來(lái)欣賞這意味著你已經(jīng)走了多遠(yuǎn)!

用這個(gè)替換模板EditCards結(jié)構(gòu):

這幾乎全部EditCards完成,但在我們可以使用它之前,我們需要添加更多代碼到ContentView,以便它顯示按需顯示工作表并在關(guān)閉時(shí)調(diào)用resetCards()

我們之前使用過工作表,但我希望你向你展示一項(xiàng)額外的技術(shù):你可以將一個(gè)功能附加到你的工作表,該功能將在工作表關(guān)閉時(shí)自動(dòng)運(yùn)行。這對(duì)你需要從工作表傳回?cái)?shù)據(jù)的時(shí)間沒有幫助,但在這里我們只是要調(diào)用resetCards()所以它是完美的。

在ContentView的最外層ZStack的末尾添加這個(gè)sheet()修飾符:

這行得通,但既然你在 SwiftUI 中獲得了更多經(jīng)驗(yàn),我想向你展示另一種獲得相同結(jié)果的方法。

當(dāng)我們使用修飾符sheet()時(shí),我們需要為 SwiftUI 提供一個(gè)它可以運(yùn)行的函數(shù),該函數(shù)返回要在工作表中顯示的視圖。對(duì)于上面的我們來(lái)說(shuō),這是一個(gè)帶有EditCards()內(nèi)部的閉包——它創(chuàng)建并返回一個(gè)新視圖,這就是工作表想要的。

當(dāng)我們編寫EditCards()時(shí),我們依賴于語(yǔ)法糖——我們將我們的視圖結(jié)構(gòu)視為一個(gè)函數(shù),因?yàn)?Swift 默默地將其視為對(duì)視圖初始化程序的調(diào)用。所以,實(shí)際上我們實(shí)際上是在寫EditCards.init(),只是用一種更短的方式。

這一切都很重要,因?yàn)槲覀儗?shí)際上可以將初始化程序EditCards直接傳遞給工作表,而不是創(chuàng)建一個(gè)調(diào)用初始化程序的閉包EditCards,如下所示:

這意味著“當(dāng)你想讀取工作表的內(nèi)容時(shí),調(diào)用EditCards初始化程序,它會(huì)把視圖發(fā)回給你使用。”

重要提示:這種方法之所以有效,是因?yàn)?strong>EditCards有一個(gè)不接受任何參數(shù)的初始化程序。如果你需要傳入特定值,則需要改用基于閉包的方法。

不管怎樣,除了resetCards()在工作表關(guān)閉時(shí)調(diào)用,我們還想在視圖首次出現(xiàn)時(shí)調(diào)用它,所以在前一個(gè)修飾符下面添加這個(gè)修飾符:

因此,當(dāng)視圖首次顯示時(shí)resetCards()被調(diào)用,當(dāng)它EditCards被關(guān)閉后顯示時(shí)resetCards()也被調(diào)用。這意味著我們可以放棄我們的示例cards數(shù)據(jù),而是將其設(shè)為一個(gè)在運(yùn)行時(shí)填充的空數(shù)組。

因此,將的cards屬性更改ContentView為:

最后,ContentView,我們需要讓cards按需加載該屬性。這從我們剛剛添加的相同代碼開始EditCard,所以現(xiàn)在把這個(gè)方法放到ContentView

現(xiàn)在我們可以添加對(duì)?resetCards()中對(duì)loadData()的調(diào)用,以便cards在應(yīng)用啟動(dòng)或用戶編輯卡片時(shí)用所有保存的卡片重新填充該屬性:

現(xiàn)在繼續(xù)運(yùn)行應(yīng)用程序。我們刪除了默認(rèn)示例,因此你需要按 + 圖標(biāo)添加一些你自己的示例。

完成最后的更改后,我們的應(yīng)用程序就完成了——干得好!




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

分享到微博請(qǐng)遵守國(guó)家法律
庆安县| 自治县| 无锡市| 岐山县| 思南县| 河东区| 肃南| 河曲县| 和政县| 乐平市| 彰武县| 平利县| 肥东县| 普定县| 新兴县| 开平市| 阿拉善右旗| 道孚县| 嘉善县| 通河县| 星子县| 张家口市| 克拉玛依市| 榆林市| 邢台县| 沂水县| 五峰| 绥阳县| 曲水县| 基隆市| 称多县| 临朐县| 汝州市| 荣成市| 土默特右旗| 许昌市| 茶陵县| 海安县| 安顺市| 彭山县| 台南市|