以下所有代码都是使用Xcode Version 6.0.1 (6A317)编写的。
由于团队开发的时候使用stroyboard在合并的时候有诸多不便,所有还是使用.xib文件编写这个ToDo App.
想要实现的功能是:TableView 上可以增加待做选项,并按照时间先后排序,可以实现删除,到点通知功能。
想要实现的效果如下:
步骤:
1、新建一个基于Singal View Application 的工程,然后删掉storyboard,在新建两个新文件 Main.xib 和 Main.swift 作为主要的ViewController,打开 Main.xib 将 File's Owner的l类属性改为 Main(这样才可以将关联变量拖动到 Mian.swift )。
Main.xib 页面UI,一个用于展示todo list 的 tableView,然后关联一个 tableView 变量到 Main.swift文件
2、接下来设置 Mian 为rootViewController,在AppDelegate.swift中做写如下代码:
- func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
- var viewController = Main(nibName: "Main", bundle: nil)
- navigationController = UINavigationController(rootViewController: viewController)
- self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
- self.window?.rootViewController = navigationController
- self.window?.makeKeyAndVisible()
- return true
- }
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var viewController = Main(nibName: "Main", bundle: nil)
navigationController = UINavigationController(rootViewController: viewController)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
return true
}
注意: var viewController = Main(nibName: "Main", bundle: nil) ,用来将 Mian.xib 与 Mian.swift 进行绑定。run 一下你就可以看到界面了。
3、然后在Main.swift 中编写一下TableView 的数据源和代理的方法。这里我们用的是 自定义的 Cell。所有新建一个 Cell.xib 和 Cell.swift 并将它们关联起来,做法和上面的相同,Cell.xib UI 如下。
- func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return 20
- }
- func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
- var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? Cell
- var str: String
- if (cell == nil) {
- let nibs:NSArray = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)
- cell = nibs.lastObject as? Cell
- }
- cell?.todoTitle.text = "toDoTitle"
- cell?.time.text = "\(NSDate())"
- cell?.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
- return cell!
- }
- func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
- }
- func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
- if editingStyle == UITableViewCellEditingStyle.Delete {
- }
- }
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? Cell
var str: String
if (cell == nil) {
let nibs:NSArray = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)
cell = nibs.lastObject as? Cell
}
cell?.todoTitle.text = "toDoTitle"
cell?.time.text = "\(NSDate())"
cell?.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
}
}
run 一下就可以看到如下效果:
注意:考虑到UITableView的滚动性能,Cell 的重用非常重要,通过上面的 println(cell),滚动Cell,观察打印出来的 Cell 地址,可以看到 Cell 并没有进行重用。在
override func viewDidLoad() { } 中添加下面的代码使 Cell 重用。
- var bundle: NSBundle = NSBundle.mainBundle()
- var nib: UINib = UINib(nibName: "Cell", bundle: bundle)
- tableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
var bundle: NSBundle = NSBundle.mainBundle()
var nib: UINib = UINib(nibName: "Cell", bundle: bundle)
tableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
4、以上讲到的都是些静态的数据,接下来我们做一些动态数据。
4.1、在NavigationBar 增加一个 ‘+’ 按钮,用来给用户增加待做选项
- self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "addItem")
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "addItem")
响应函数:
- func addItem() {
- let addVC: Add = Add(nibName: "Add", bundle: nil)
- addVC.delegate = self;
- self.presentViewController(addVC, animated: true, completion: nil)
- }
func addItem() {
let addVC: Add = Add(nibName: "Add", bundle: nil)
addVC.delegate = self;
self.presentViewController(addVC, animated: true, completion: nil)
}
4.2、新增一个 Add.xib 和 Add.swift 让用户输入待做选项,记得绑定(同步骤1),Add.xib UI如下:
为了在Main.swift 中接收到 Add.xib 中用户输入的信息,我们在 Add.swift 定义一个协议,然后Main.swift 遵循这个协议,在Add.xib 界面消失前获取用户输入信息。
- protocol AddProtocal {
- func didCompleted(addObject: Add)
- }
protocol AddProtocal {
func didCompleted(addObject: Add)
}
Add.swift 代码如下:
- //
- // Add.swift
- // ToDoApp
- //
- // Created by aaron on 14-9-17.
- // Copyright (c) 2014年 The Technology Studio. All rights reserved.
- //
- import UIKit
- protocol AddProtocal {
- func didCompleted(addObject: Add)
- }
- class Add: UIViewController {
- @IBOutlet var todo: UITextField!
- @IBOutlet var desc: KCTextView!
- @IBOutlet var time: UIDatePicker!
- @IBOutlet var completeBtn: UIButton!
- var delegate: AddProtocal?
- required init(coder aDecoder: NSCoder) {
- super.init(coder: aDecoder)
- }
- override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
- super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
- }
- override func viewWillAppear(animated: Bool) {
- setup()
- }
- func setup() {
- completeBtn.layer.cornerRadius = 5.0
- todo.placeholder = "请输入待做项"
- // desc.placeholder = "请输入详细描述。"
- todo.text = self.todo.text
- desc.text = self.desc.text
- time.date = self.time.date
- time.minimumDate = NSDate.date()
- if delegate? == nil {
- todo.textColor = UIColor.lightGrayColor()
- todo.userInteractionEnabled = false
- desc.textColor = UIColor.lightGrayColor()
- desc.userInteractionEnabled = false
- time.userInteractionEnabled = false
- completeBtn.setTitle("好", forState: UIControlState.Normal)
- }else {
- todo.textColor = UIColor.blackColor()
- todo.userInteractionEnabled = true
- desc.textColor = UIColor.blackColor()
- desc.userInteractionEnabled = true
- time.userInteractionEnabled = true
- completeBtn.setTitle("完成", forState: UIControlState.Normal)
- }
- let swipeGesture = UISwipeGestureRecognizer(target: self, action:"hideKeyboard")
- swipeGesture.direction = UISwipeGestureRecognizerDirection.Down
- swipeGesture.numberOfTouchesRequired = 1
- self.view.addGestureRecognizer(swipeGesture)
- }
- func hideKeyboard() {
- println("swipeGesture....")
- todo.resignFirstResponder()
- desc.resignFirstResponder()
- }
- func shakeAnimation(sender: AnyObject) {
- let animation = CAKeyframeAnimation()
- animation.keyPath = "position.x"
- animation.values = [0, 10, -10, 10, 0]
- animation.keyTimes = [0, 1/6.0, 3/6.0, 5/6.0, 1]
- animation.duration = 0.4
- animation.additive = true
- sender.layer.addAnimation(animation, forKey: "shake")
- }
- @IBAction func completeTouch(sender: AnyObject) {
- if (countElements(todo.text) > 0){
- delegate?.didCompleted(self)
- self.dismissViewControllerAnimated(true, completion: nil)
- }else{
- shakeAnimation(todo)
- }
- }
- @IBAction func editingDidEnd(sender: UITextField) {
- if (countElements(sender.text) == 0) {
- shakeAnimation(todo)
- }
- }
- }
//
// Add.swift
// ToDoApp
//
// Created by aaron on 14-9-17.
// Copyright (c) 2014年 The Technology Studio. All rights reserved.
//
import UIKit
protocol AddProtocal {
func didCompleted(addObject: Add)
}
class Add: UIViewController {
@IBOutlet var todo: UITextField!
@IBOutlet var desc: KCTextView!
@IBOutlet var time: UIDatePicker!
@IBOutlet var completeBtn: UIButton!
var delegate: AddProtocal?
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
override func viewWillAppear(animated: Bool) {
setup()
}
func setup() {
completeBtn.layer.cornerRadius = 5.0
todo.placeholder = "请输入待做项"
// desc.placeholder = "请输入详细描述。"
todo.text = self.todo.text
desc.text = self.desc.text
time.date = self.time.date
time.minimumDate = NSDate.date()
if delegate? == nil {
todo.textColor = UIColor.lightGrayColor()
todo.userInteractionEnabled = false
desc.textColor = UIColor.lightGrayColor()
desc.userInteractionEnabled = false
time.userInteractionEnabled = false
completeBtn.setTitle("好", forState: UIControlState.Normal)
}else {
todo.textColor = UIColor.blackColor()
todo.userInteractionEnabled = true
desc.textColor = UIColor.blackColor()
desc.userInteractionEnabled = true
time.userInteractionEnabled = true
completeBtn.setTitle("完成", forState: UIControlState.Normal)
}
let swipeGesture = UISwipeGestureRecognizer(target: self, action:"hideKeyboard")
swipeGesture.direction = UISwipeGestureRecognizerDirection.Down
swipeGesture.numberOfTouchesRequired = 1
self.view.addGestureRecognizer(swipeGesture)
}
func hideKeyboard() {
println("swipeGesture....")
todo.resignFirstResponder()
desc.resignFirstResponder()
}
func shakeAnimation(sender: AnyObject) {
let animation = CAKeyframeAnimation()
animation.keyPath = "position.x"
animation.values = [0, 10, -10, 10, 0]
animation.keyTimes = [0, 1/6.0, 3/6.0, 5/6.0, 1]
animation.duration = 0.4
animation.additive = true
sender.layer.addAnimation(animation, forKey: "shake")
}
@IBAction func completeTouch(sender: AnyObject) {
if (countElements(todo.text) > 0){
delegate?.didCompleted(self)
self.dismissViewControllerAnimated(true, completion: nil)
}else{
shakeAnimation(todo)
}
}
@IBAction func editingDidEnd(sender: UITextField) {
if (countElements(sender.text) == 0) {
shakeAnimation(todo)
}
}
}
ToDo项为空时会有一个小小的提示动画:
Add.swift 中的关联变量 desc 是UITextView 类型的,UITextView 不像 UITextField 有 placeHolder ,所以这里我们引入一个 OC 写的 KCTextView ,由 KCTextView 代替 UITextView,swift 中引用 OC 写的 API 容易,新建一个 .h ,把你需要用到的头文件统统写在里面,然后 Build Settings 中的 Object-C Bridging Header 写入 .h 文件的路径即可,接着就可以正常使用 OC 写的接口了。
Main.swift 实现 AddProtocal,并实现协议规定的函数:
- func didCompleted(addObject: Add) {
- toDoData.append(addObject)
- tableView.reloadData()
- }
func didCompleted(addObject: Add) {
toDoData.append(addObject)
tableView.reloadData()
}
toDoData的是一个 Add类型的可变数组。
Main.swift 代码如下:
- //
- // Main.swift
- // ToDoApp
- //
- // Created by aaron on 14-9-16.
- // Copyright (c) 2014年 The Technology Studio. All rights reserved.
- //
- import UIKit
- class Main: UIViewController, UITableViewDataSource, UITableViewDelegate, AddProtocal {
- @IBOutlet var tableView: UITableView!
- let cellIdentifier = "Cell"
- var toDoData = [Add]()
- override func viewDidLoad() {
- super.viewDidLoad()
- setup()
- registerCell()
- }
- func setup() {
- self.title = "To Do List"
- self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "addItem")
- }
- func registerCell() {
- var bundle: NSBundle = NSBundle.mainBundle()
- var nib: UINib = UINib(nibName: "Cell", bundle: bundle)
- tableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
- }
- func addItem() {
- let addVC: Add = Add(nibName: "Add", bundle: nil)
- addVC.delegate = self;
- self.presentViewController(addVC, animated: true, completion: nil)
- }
- func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return toDoData.count
- }
- func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
- var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? Cell
- var str: String
- if (cell == nil) {
- let nibs:NSArray = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)
- cell = nibs.lastObject as? Cell
- }
- let addObject = toDoData[indexPath.row] as Add
- cell?.todoTitle.text = addObject.todo.text
- cell?.time.text = dateFormatter(addObject.time.date)
- cell?.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
- return cell!
- }
- func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
- let addVC = toDoData[indexPath.row] as Add
- addVC.delegate = nil
- self.presentViewController(addVC, animated: true, completion: nil)
- }
- func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
- if editingStyle == UITableViewCellEditingStyle.Delete {
- toDoData.removeAtIndex(indexPath.row)
- tableView.reloadData()
- }
- }
- func didCompleted(addObject: Add) {
- toDoData.append(addObject)
- toDoData.sort({ self.dateFormatter($0.time.date) < self.dateFormatter($1.time.date)})//按时间排序
- tableView.reloadData()
- }
- func dateFormatter(date: NSDate) -> String {
- let formatter = NSDateFormatter()
- formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
- formatter.locale = NSLocale(localeIdentifier: NSGregorianCalendar)
- let dateStr = formatter.stringFromDate(date)
- return dateStr
- }
- override func didReceiveMemoryWarning() {
- super.didReceiveMemoryWarning()
- // Dispose of any resources that can be recreated.
- }
- }
//
// Main.swift
// ToDoApp
//
// Created by aaron on 14-9-16.
// Copyright (c) 2014年 The Technology Studio. All rights reserved.
//
import UIKit
class Main: UIViewController, UITableViewDataSource, UITableViewDelegate, AddProtocal {
@IBOutlet var tableView: UITableView!
let cellIdentifier = "Cell"
var toDoData = [Add]()
override func viewDidLoad() {
super.viewDidLoad()
setup()
registerCell()
}
func setup() {
self.title = "To Do List"
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "addItem")
}
func registerCell() {
var bundle: NSBundle = NSBundle.mainBundle()
var nib: UINib = UINib(nibName: "Cell", bundle: bundle)
tableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
}
func addItem() {
let addVC: Add = Add(nibName: "Add", bundle: nil)
addVC.delegate = self;
self.presentViewController(addVC, animated: true, completion: nil)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return toDoData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? Cell
var str: String
if (cell == nil) {
let nibs:NSArray = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)
cell = nibs.lastObject as? Cell
}
let addObject = toDoData[indexPath.row] as Add
cell?.todoTitle.text = addObject.todo.text
cell?.time.text = dateFormatter(addObject.time.date)
cell?.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let addVC = toDoData[indexPath.row] as Add
addVC.delegate = nil
self.presentViewController(addVC, animated: true, completion: nil)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
toDoData.removeAtIndex(indexPath.row)
tableView.reloadData()
}
}
func didCompleted(addObject: Add) {
toDoData.append(addObject)
toDoData.sort({ self.dateFormatter($0.time.date) < self.dateFormatter($1.time.date)})//按时间排序
tableView.reloadData()
}
func dateFormatter(date: NSDate) -> String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.locale = NSLocale(localeIdentifier: NSGregorianCalendar)
let dateStr = formatter.stringFromDate(date)
return dateStr
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
最后你大概可以看到这样的效果:
5、最后一步,为待做项目添加通知功能,这一功能在之前的文章(ios8 notifacation in swift)中就讲过了,这里就不重复写了。完整的项目代码我发在github上来,需要的到这里拿。