CocoaTouch 开发 - GaoZhenWei/SwiftLearning GitHub Wiki

1. UITableView的使用关键点记录

1.1 UITableView vs UITableViewController

  • UITableView是一个UIView的子类,需要将其放到ViewController中,并手动设置其delegate与datasource
  • UITableViewController是一个继承自UIViewController的试图控制器,它自动包含了一个UITableView作为其view,而且自动关联了delegate和datasource到它自身
  • UITableView使用起来更加的灵活,可以布置复杂的布局,但是需要我们手动设置其delegate与datasource,并实现其方法
  • UITableViewController就只能用于创建只使用UITableView的控制器,相对限制了我们的UI界面。
  • 在日常开发过程中,尽量使用UITableView来应对经常变化的需求

1.2 UITableViewCell的复用机制

苹果为了减少内存的消耗与界面的流畅性,引入了Cell的复用机制,其使用步骤为:

  • Step 1:在viewDidLoad中使用方法registerClass:forCellReuseIdentifier:注册需要复用的Cell类并指定一个CellReuseIdentifier
let CellIdentifier = "MyFirstTableViewCell"
override func viewDidLoad() {
    super.viewDidLoad()
    tableView.dataSource = self
    tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: CellIdentifier)
}
  • Step 2:在cellForRowAtIndexPath方法中,使用dequeueReusableCellWithIdentifier:forIndexPath:来获取到一个可以复用的Cell:
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath) as UITableViewCell
        cell.textLabel?.text = data[indexPath.row]
        return cell
    }

Notes:在复用某个Cell类时,一定要先在viewDidLoad中先注册这个类以及用来复用的CellIdentifier,否则程序会Crash。

1.3 创建UITableViewCell的子类,实现自定义Cell

方式一:在Storyboard中创建并实现自定义Cell的UI布局
  • Step 1:创建一个集成在UITableViewCell的子类的Swift文件
  • Step 2:在Storyboard中拖入一个Cell,并设置其Custom Class为上一步创建的Swift类
  • Step 3:在Storyboard中选中这个Cell,在属性栏的identifier中指定一个CellIdentifier
  • Step 4:在cellForRowAtIndexPath方法中,使用dequeueReusableCellWithIdentifier:forIndexPath:来获取到一个可以复用的Cell,同时需要将默认的UITableViewCell转换为自定义的Swift类型:
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("com.codepath.DemoPrototypeCell", forIndexPath: indexPath) as DemoPrototypeCell
        let cityState = data[indexPath.row].componentsSeparatedByString(", ")
        cell.cityLabel.text = cityState.first
        cell.stateLabel.text = cityState.last
        return cell
    }

Notes:使用Storyboard布局的自定义Cell,无须在viewDidLoad中注册,只需要在Storyboard的属性栏中为其指定一个CellIdentifier即可。

方式二:为自定义Cell创建一个单独的Xib文件进行UI布局

如果项目没有使用Storyboard,可以创建一个NIB文件来进行布局,和在Storyboard中设置Cell一样,也需要设置其Custom Class。不一样的地方在于:无须指定其identifier,指定了也是没用的,这是因为在使用这个Cell时,需要先在viewDidLoad中加载并注册这个Nib文件,同时指定一个identifier:

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        let cellNib = UINib(nibName: "DemoNibTableViewCell", bundle: NSBundle.mainBundle())
        tableView.registerNib(cellNib, forCellReuseIdentifier: "MyNibTableViewCell")
    }
方式三:使用纯代码为自定义Cell进行UI布局
  • Step 1:重写init(style:reuseIdentifier),在里面添加子视图,并进行布局
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        initViews()
    }
  • Step 2:重写drawRect:方法,进行图形的绘制;或者重写layoutSubviews:进行子视图的Frame的设置
  • Step 3:在使用此Cell的试图控制器中的viewDidLoad方法中,进行注册类以及复用时的identifier:
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.registerClass(DemoProgrammaticTableViewCell.self, forCellReuseIdentifier: "MyProgrammaticCell")
    }

1.4 自定义的UITableViewCell的子类响应selected的方式

  • 首先,最基本的可以设置cell的selectionStyle
  • 通过UITableViewallowsSelection来控制是否能被选中
  • 通过设置Cell的selectedBackgroundView属性来改变Cell被选中时的背景
  • 在自定义的Cell类中,重写setSelected方法来执行Cell被选中时的一些操作
import UIKit

class DemoProgrammaticTableViewCell: UITableViewCell {

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        initViews()

    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initViews()
    }

    func initViews() {
        selectedBackgroundView=UIView(frame: frame)
        selectedBackgroundView.backgroundColor = UIColor(red: 0.5, green: 0.7, blue: 0.9, alpha: 0.8)
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        let fontSize: CGFloat = selected ? 34.0 : 17.0
        self.textLabel?.font = self.textLabel?.font.fontWithSize(fontSize)
    }
}

1.5 UIRefreshControl--下拉刷新控件的使用

1.5.1 在UITableViewController中使用

UIKit中提供了这个标准的下拉刷新控件,但是这个控件是被设计用于UITableViewController及其子类中,其使用方式如下:

class TableViewController: UITableViewController {
    var stories: [Story] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        fetchStories()
        refreshControl = UIRefreshControl()
        refreshControl?.addTarget(self, action: "fetchStories", forControlEvents: UIControlEvents.ValueChanged)
    }

    func fetchStories() {
        Story.fetchStories({ (stories: [Story]) -> Void in
            dispatch_async(dispatch_get_main_queue(), {
                self.stories = stories
                self.tableView.reloadData()
                self.refreshControl?.endRefreshing()
            })
        }, error: nil)
    }
    ...
}
1.5.2 在__非__UITableViewController中使用

虽然此控件被设计用于UITableViewController中,但是我们可以稍微做一些“手脚”,就能够使用到其他的视图控制器中:

class ViewController: UIViewController, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!
    var refreshControl: UIRefreshControl!
    var stories: [Story] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self

        refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: "fetchStories", forControlEvents: UIControlEvents.ValueChanged)

        let dummyTableVC = UITableViewController()
        dummyTableVC.tableView = tableView
        dummyTableVC.refreshControl = refreshControl
    }
}