Swift90Days - 1个简单的小利用
第0步:明确任务
经过前面基础语法的学习,我们终究可以真枪实弹的来1发了。以这篇小鸡鸡小猫猫小狗狗为例,我们将会创建1个简单的利用:
- 通过
UITableView
展现3种小动物
- 通过
NSUserDefaults
进行数据持久化
- 通过
UIWebView
从维基百科加载更多数据
由于时间有限,这篇博客其实不是教程或翻译,只是1个关键思路的整理和记录,完全的源代码在文末有链接,如果有任何疑问欢迎与我联系,谢谢。
第1步:创建项目
创建1个新的项目,模板选择 Single View Application
,项目名称叫做:BirdsCatsDogs。
第2步:简单重构
系统为我们自动生成了 ViewController.swift
,将其改成SpeciesViewController.swift
,记得也改下类的名字。然后在 StoryBoard (以后简称为 SB 希望不要误解) 中设置 custum class
,如果设置正确在输入的时候是有自动补全的,回车便可。
第3步:添加导航
拖拽1个 UINavigationController 到 SB 中,设置成 Initial View Controller
,然后把 SpeciesViewController
设置成它的 root view controller
。把 SpeciesViewController
的 title 设置成 Species
。
运行1下,确保没有问题。(不可能有问题,这时候候运行1般是满足自己内心的成绩感。)
第4步:加载数据
在这里我们用 NSUserDefaults
加载数据,通常它用来存储1些系统配置,比如字体大小啊之类的。
我们新建1个 DataManager.swift
,通过单例模式实现1个数据管理器:
import Foundation class DataManager { struct Static { static var onceToken : dispatch_once_t = 0 static var instance : DataManager? = nil } class var sharedInstance : DataManager { dispatch_once(&Static.onceToken) { Static.instance = DataManager() } return Static.instance! } }
这段代码是原文的代码,有些地方可以参考:
- 静态变量通过内嵌
Static
结构体存储。
- 单例模式通过
dispatch_once
实现,通过 sharedInstance
获得。 (GCD的内容后面再补充)
接下来我们在 DataManager
里面添加1个变量:species
,类型为 [String:[String]]
。在 init()
里加上1些初始化的工作:
var species: [String:[String]] init() { let userDefaults = NSUserDefaults.standardUserDefaults() if let speciesInfo = userDefaults.valueForKey("species") as? [String:[String]] { species = speciesInfo } else { species = [ "Birds": ["Swift"], "Cats" : ["Persian Cat"], "Dogs" : ["Labrador Retriever"] ] } }
我们可以通过 DataManager.sharedInstance.species
获得各个种类的数据。
Tips:类似于单例模式这类可能会屡次用到的代码片断,建议加到 Xcode 的 Snippets 里。
第5步:加载列表
我们用字典存储了数据,通过 key 值获得数据10分方便。但是字典本身是无序的,而像 UITableView 这类列表的数据本身是有序的。所以添加1个计算属性 speciesList
,可以获得排序以后的列表并返回:
var speciesList: [String] { var list: [String] = [] for speciesName in species.keys { list.append(speciesName) } list.sort(<) return list }
回到 SpeciesViewController
里,我们可以这样获得数据:
var species: [String] = DataManager.sharedInstance.speciesList
第6步:列表视图
拖拖拽拽设置好 UITableView
,具体进程就不赘述了,可以直接打开项目看看。tableview
相干的部份代码以下:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell cell.textLabel?.text = species[indexPath.row] return cell } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return species.count }
运行1下,确保没有问题。(这时候候就不是成绩感的问题了,测试 SB 和代码的连接情况。)
第7步:详情页面
我们再创建1个 RacesViewController
,用来展现当前种类下的数据列表:
class RacesViewController: UIViewController { var species: String! override func viewDidLoad() { super.viewDidLoad() title = species } }
注意在 StoryBoard 里设置这个 RacesViewController
的 StoryBoard ID ,这样我们在做点击事件的时候可以获得到这个 RacesViewController
然落后行 pushViewController
操作。
第8步:选中事件
回到 SpeciesViewController
里,添加单元格的选中事件:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: true) var racesViewController = storyboard?.instantiateViewControllerWithIdentifier("RacesViewController") as RacesViewController racesViewController.species = species[indexPath.row] navigationController?.pushViewController(racesViewController, animated: true) }
instantiateViewControllerWithIdentifier
可以通过 StoryBoard ID 初始化 ViewController 。
第9步:展现种类
这个步骤和第6步基本相同, tableview
相干的代码:
func tableView
(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return races.count } func tableView
(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(
"RaceCell") as UITableViewCell cell.textLabel?.text = races[indexPath.row] cell.
accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell }
这时候再测试1下,点击 cell 以后会跳转到另外一个表格列表里。
第10步:保存修改
给 DataManager
加个添加的方法:
func saveData() { let userDefaults = NSUserDefaults.standardUserDefaults() userDefaults.setValue(species, forKey: "species") } func addRace(species inSpecies: String, race: String) { if var races = species[inSpecies] { races.append(race) species[inSpecies] = races } saveData() }
saveData
方法用来写入本地,addRace
方法供外部调用,添加1条记录。
第11步:添加按钮
给导航栏加个 Add 按钮,并且关联 didTapAdd
这个 IBAction
。
第12步:弹出视图
使用 UIAlerView
弹出视图输入内容,注意设置 style 为 PlainTextInput
,设置 delegate 为 self 。
@IBAction func didTapAdd() { var alert = UIAlertView(title: "New Race", message: "Type in a new race", delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "Add") alert.alertViewStyle = UIAlertViewStyle.PlainTextInput alert.show() }
然后实现 alertView
的拜托方法:
func alertView(alertView: UIAlertView, didDismissWithButtonIndex buttonIndex: Int) { if buttonIndex == 1 { var textField = alertView.textFieldAtIndex(0)! var newRace = textField.text DataManager.sharedInstance.addRace(species: species, race: newRace) var newIndexPath = NSIndexPath(forRow: races.count - 1, inSection: 0) myTableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic) } }
这个时候再运行1下测试1下添加功能是否是OK。
第13步:删除数据
和添加数据1样,我们先去 DataManager 加个删除数据的方法:
func removeRace(species inSpecies: String, race inRace: String) { if var races = species[inSpecies] { var index = -1 for (idx, race) in enumerate(races) { if race == inRace { index = idx break } } if index != -1 { races.removeAtIndex(index) species[inSpecies] = races saveData() } } }
有几个值得注意的地方:
- 通过
index
设置为 ⑴ 作为标识,避免出现搜索到最后也没找到的结局。
- 通过
enumerate
来遍历数组,既不用 ++i
式的遍历,又可以获得索引值。
然后回到 RacesViewController
,添加删除相干的拜托方法:
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { return true } func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { var raceToRemove = races[indexPath.row] DataManager.sharedInstance.removeRace(species: species, race: raceToRemove) tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) }
试1下删除操作,OK没问题。
完结
后面 WebView 的内容没甚么亮点,大同小异,不再记录。
匆匆记录了1些关键点,如果想完全学习请参考援用中的教程。
其实全部项目的功能很简单,不过凑头到尾走1遍可以体验1下其他程序员的开发思路和基本步骤。总之还是有些收获的。不过边看边记录效力太低,以后只会记录关键的收获,不再