在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
對許多剛學會 App 開發技術的初學者來說,他們懂得 Swift 語法,也熟悉各種常見功能的 iOS SDK,但在實際開發 App 時,卻常遭遇 2 個問題: 不知如何寫出容易理解和維護的程式。 遇到問題時,想到四五種解法,不知該用哪一種。 要解決這兩個問題,最好的方法莫過於參考大大們的 App 大作,學習模仿他們的程式碼。然而有時神人們可能會採用一些高深莫測的技術,讓初學者難以理解。對初學者來說,也許 Apple 官方的教學電子書會是更好的選擇。因為 Apple 出品,品質自然不在話下,又因是給初學者學習的教學範例,採用的也將是初學者容易理解掌握的做法。 在这里我还是要推荐下我自己建的iOS开发学习群:680565220,群里都是学ios开发的,如果你正在学习ios ,小编欢迎你加入,今天分享的这个案例已经上传到群文件,大家都是软件开发党,不定期分享干货(只有iOS软件开发相关的),包括我自己整理的一份2018最新的iOS进阶资料和高级开发教程 Apple 的電子書提到大部分 App 常見的功能,比方資料的讀取新增修改,資料的儲存,從網路抓取資料等。若能完全掌握書裡介紹的技巧,開發一些基本功能的 App 應該完全不是問題。接下來的文章裡,我將列出一些書裡值得參考模仿的重點,希望能幫助大家更方便抄襲。讓我們一起來模仿 Apple 大大,寫出一手好 Swift! Intro to App Development with Swift App Development with Swift 變數,function,型別的命名 開發 iOS App 時,如何為變數,型別,function 命名,一直是件頭大的事。為了清楚表達意思,名字常常以多個單字組成,並以 Camel case 方法命名,每個單字的開頭大寫,第一個單字例外,比方顯示答案的變數 resultAnswerLabel。(camel 的意思是駱駝,當單字的字首大寫,多個單字組合起來時,每個單字的字首就像駱駝的駝峰,十分可愛。) 此方法的好處在於我們更容易看出名稱由哪幾個單字組成,方便看懂名稱的意思。第一個單字的字首小寫,則是因為 Swift 習慣上只有型別名稱的字首大寫。 至於什麼才是好名字,依 Apple 的教學範例,可整理出以下幾點常見的規則: 1. 自訂的類別繼承父類別時,類別名稱以父類別的名稱結尾。 以 ViewController 結尾。 2. 畫面上的 UI 元件,其變數名稱結尾和型別有關。 [email protected] weak var questionLabel: UILabel! [email protected] weak var rangedSlider: UISlider! 3. UI 元件事件觸發的 function 名和事件有關。 1var categories = [String]() 共用資料宣告成型別常數,取名為 shared 或 default。 App 裡有些負責特定功能的物件會在多個頁面使用,比方抓取網路資料的物件。你可將它宣告成只會建立一次的型別常數,省去每次使用時重新生成的麻煩,並享有任何地方皆可方便存取的好處,就像以下例子的 MenuController.shared iOS SDK 本身就有很多類似例子,比方 URLSession.shared,UIApplication.shared, FileManager.default。 將字串定義成型別常數 開發 iOS App 時,總有某些東西是我們無法避免,必須以字串輸入的,比方 segue ID,cell ID,storyboard ID 等。然而只要你一不小心打錯,將產生非常可怕的後果,輕則功能失效,重則讓 App 閃退,地球毀滅 ! 因此,不妨參考 Apple 的做法,將字串定義成型別常數,到時輸入時 Xcode 將幫我們自動完成,一輩子都不會打錯。 讓我們看看以下幾個例子: 1. segue ID 和 cell ID 在 controller 裡以 struct 定義型別 PropertyKeys,宣告屬性儲存 segue ID 和 cell ID。 class AthleteTableViewController: UITableViewController { struct PropertyKeys { static let athleteCell = "AthleteCell" static let addAthleteSegue = "AddAthlete" static let editAthleteSegue = "EditAthlete" } 以 struct 定義型別 SegueID, 宣告屬性儲存 segue ID。 struct SegueID { static let topicPicker = "TopicPickerController" static let mainShowDetail = "ShowDetail" static let mainAddNew = "AddNew" } 2. Storyboard ID 以 struct 定義型別 StoryboardID,宣告屬性儲存 storyboard ID。 struct StoryboardID { static let main = "Main" static let mainNC = "MainNC" static let zoneNC = "ZoneNC" static let note = "Note" static let noteNC = "NoteNC" } 在 controller 裡宣告屬性 storyboardIdentifier 儲存它的 storyboard ID。 class BuildIceCreamViewController: UIViewController { static let storyboardIdentifier = "BuildIceCreamViewController" } 3. Notification name 定義Notification.Name 的 extension,宣告屬性儲存自訂的通知名稱。 extension Notification.Name { static let zoneCacheDidChange = Notification.Name("zoneCacheDidChange") static let topicCacheDidChange = Notification.Name("topicCacheDidChange") } 4. Dictionary 的 key。 以 struct 定義型別 NotificationObjectKey,宣告屬性儲存 Notification 的 userInfo 裡自訂的 key。 struct NotificationObjectKey { static let reason = "reason" static let recordIDsDeleted = "recordIDsDeleted" static let recordsChanged = "recordsChanged" static let newNote = "newNote" } 以 struct 定義型別 PropertyKey,宣告屬性儲存想要寫檔的欄位字串。 class Note: NSObject, NSCoding { let title: String let text: String let timestamp: Date struct PropertyKey { static let title = "title" static let text = "text" static let timestamp = "timestamp" 將設定畫面內容的程式定義成 update 開頭的 function。 controller 一般會有一段設定畫面內容的程式,而且會在多個時候設定,比方 viewDidLoad, viewWillAppear,或是抓到網路上的資料後。所以你可將設定畫面內容的程式另外定義成 update 開頭的 function,如此需要設定畫面內容時,只要呼叫此 function 即可。 func updateUI() { let currentQuestion = questions[questionIndex] questionLabel.text = currentQuestion.text } override func viewDidLoad() { super.viewDidLoad() updateUI() } func nextQuestion() { questionIndex += 1 if questionIndex < questions.count { updateUI() } else { performSegue(withIdentifier: SegueID.resultsSegue, sender: nil) } } 在 viewDidLoad 和 nextQuestion 裡呼叫 updateUI()。 搭配 guard let 建立自訂型別的 cell 建立自訂類別的 cell 時,如果你很有信心,覺得不可能失敗,一般會使用 as! 強制轉型。 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.loverCell, for: indexPath) as! BookTableViewCell let book = books[indexPath.row] cell.update(with: book) return cell } 其實有更安全的做法,你可以用 guard let 讀取搭配 as? 轉型的 cell。 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.loverCell, for: indexPath) as? BookTableViewCell else { fatalError("Could not dequeue a cell") } let book = books[indexPath.row] cell.update(with: book) return cell } 將設定 cell 顯示內容的程式定義成 function 在 controller 裡定義設定 cell 內容的 function,如以下例子裡的 configure(cell:forItemAt:)。 func configure(cell: UITableViewCell, forItemAt indexPath: IndexPath) { let categoryString = categories[indexPath.row] cell.textLabel?.text = categoryString.capitalized } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.categoryCellIdentifier, for: indexPath) configure(cell: cell, forItemAt: indexPath) return cell } 在自訂的 cell 類別裡定義設定內容的 function,名稱以 update 開頭,參數為要顯示的資料。 class BookTableViewCell: UITableViewCell { func update(with book: Book) { titleLabel.text = book.title authorLabel.text = book.author genreLabel.text = book.genre lengthLabel.text = book.length } } class BookTableViewController: UITableViewController { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.loverCell, for: indexPath) as? BookTableViewCell else { fatalError("Could not dequeue a cell") } let book = books[indexPath.row] cell.update(with: book) return cell 資料輸入頁面以 static cell 實作 資料的輸入頁面往往有很多欄位,適合以上下捲動的表格呈現。又因欄位個數是固定的,所以可以直接用 UITableViewController 搭配 static cell 實作,享有以下幾點好處: cell 裡的輸入欄位,諸如 text field, slider,皆可設為 controler 的 outlet 變數。(若為 Dynamic Prototypes 的表格,cell 上的元件只能設為 cell 類別的 outlet 鍵盤出現時,表格會自動往上捲,text field 不會被檔到。 class BookFormTableViewController: UITableViewController { @IBOutlet weak var titleTextField: UITextField! @IBOutlet weak var authorTextField: UITextField! @IBOutlet weak var genreTextField: UITextField! @IBOutlet weak var lengthTextField: UITextField! 新增資料時 present 另一個 navigation controller iOS App 在新增資料時,常使用 present 另一個 navigation controller 的設計,例如內建的通訊錄 App 和行事曆 App。 利用 guard let 或 if let 比對多個 optional,檢查使用者輸入的內容 當 App 的表單頁面有很多欄位時,我們往往要用大量的 guard let 或 if let 確認使用者輸入的資料,例如以下例子: @IBAction func saveButtonTapped(_ sender: Any) { guard let title = titleTextField.text else { return } if title.count == 0 { return } guard let author = authorTextField.text else { return } if author.count == 0 { return } book = Book(title: title, author: author) performSegue(withIdentifier: PropertyKeys.unwind, sender: self) } 其實不用這麼麻煩,guard let 結合逗號即可一次比對多個 optional。 @IBAction func saveButtonTapped(_ sender: Any) { guard let title = titleTextField.text, title.count > 0, let author = authorTextField.text, author.count > 0 else { return } book = Book(title: title, author: author) performSegue(withIdentifier: PropertyKeys.unwind, sender: self) } 長得跟 guard let 很像的好兄弟 if let 結合逗號後,也一樣能一次比對多個 optional。 @IBAction func saveButtonTapped(_ sender: Any) { if let name = nameTextField.text, let employeeType = employeeType {
全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论