SppedUp
我们团队每隔两周会有一次 SppedUp ,两天时间开发一款全新的应用。从前天凌晨到昨天深夜整整48小时,第一次 SpeedUp 理论上应该已经结束,但是事实上并没有想象中那么理想,一直拖到今天还在继续,而且还存在诸如『登录时键盘遮挡输入框』这种低级错误。今天花了一天时间完善了细节,并且整理一下代码,整理一些 Swift 中的代码优化细节。
UIStoryboardSegue
作为一名纯粹的 IB 党,少不了和 UIStoryboardSegue
打交道。主要的使用场景是以下两个方法:
-
performSegueWithIdentifier(_:sender:)
-
prepareForSegue(_:sender:)
一开始我们是这么写的:
performSegueWithIdentifier("show_detail", sender: nil)
后来发觉这种 hard code
并不科学,于是用 struct 封装了一下定义成常量,这样合理多了:
struct SegueIdentifier {
static let showDetal = "show_detail"
}
performSegueWithIdentifier(SegueIdentifier.showDetal, sender: dataSource[indexPath.row])
再后来感觉 prepareForSegue
里不能 switch
不开心啊!于是改成用 enum
封装:
enum SegueIdentifier: String {
case Detail = "show_detail"
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard
let identifier = segue.identifier,
let segueIdentifier = SegueIdentifier(rawValue: identifier) else {
fatalError("INVALID SEGUE IDENTIFIER /(segue.identifier)")
}
switch segueIdentifier {
case .Detail:
// DO SOMETHING
}
}
但是在调用 performSegue
的时候还是有点难受,不能直接传 enum
进去,必须要 rawValue
才行:
performSegueWithIdentifier(SegueIdentifier.Detail.rawValue, sender: nil)
后来昨天看 Swift in Practice 的时候发现,其实可以利用 protocol extension
把通用的部分封装起来:
protocol SegueHandlerType {
typealias SegueIdentifier: RawRepresentable
}
extension SegueHandlerType where Self: UIViewController, SegueIdentifier.RawValue == String {
func performSegueWithIdentifier(identifier: SegueIdentifier, sender: AnyObject?) {
performSegueWithIdentifier(identifier.rawValue, sender: sender)
}
func segueIdentifierForSegue(segue: UIStoryboardSegue) -> SegueIdentifier {
guard
let identifier = segue.identifier,
let segueIdentifier = SegueIdentifier(rawValue: identifier) else {
fatalError("INVALID SEGUE IDENTIFIER /(segue.identifier)")
}
return segueIdentifier
}
}
这样只要 UIViewController
实现 SegueHandlerType
协议便可获得封装好的方法,比如更好用的 performSegueWithIdentifier
:
performSegueWithIdentifier(.LoginViewController, sender: nil)
而且 prepareForSegue
可以直接通过 switch segueIdentifierForSegue(segue)
对不同 segue
做处理:
extension RankingViewController: SegueHandlerType {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segueIdentifierForSegue(segue) {
case .Detail:
guard
let controller = segue.destinationViewController as? DetailViewController,
let cell = sender as? RankingCell,
let app = cell.appDetail else {
return
}
controller.appDetail = app
}
}
}
dequeueReusableCellWithIdentifier
在写 UITableView
的时候总会有一段很难记但是又不得不记的代码:
let cell = tableView.dequeueReusableCellWithIdentifier("XxxxxCell") as! XxxxxCell
注意这里又出现了 "XxxxxCell"
这样的 hard code
,我们可以用 Class
的名称作为它的 identifier
,这样只要能获取到 className
就行了。在 Swift 里可以这样获取 className
:
extension NSObject {
static var className: String {
get {
return self.description().componentsSeparatedByString(".").last!
}
}
}
于是乎我们的代码变成了这样:
let cell = tableView.dequeueReusableCellWithIdentifier(XxxxxCell.className) as! XxxxxCell
接下来我们可以给 UITableView
添加个扩展,封装一下上面的代码:
extension UITableView {
func dequeueCell<T: UITableViewCell>(cell: T.Type) -> T {
return dequeueReusableCellWithIdentifier(T.className) as! T
}
}
这样就可以这样生成 cell
了:
let cell = tableView.dequeueCell(RankingCell)
AVObject
由于在使用 LeanCloud
做后台服务,所以不可避免要和 AVObject
打交道, AVObject
基本和 NSDictionary
接口一致,所以做了如下封装:
import Foundation
import AVOSCloud
extension AVObject {
func stringForKey(key: String) -> String? {
return self.objectForKey(key) as? String
}
func doubleForKey(key: String) -> Double? {
let o = self.objectForKey(key)
if let r = o as? Double {
return r
} else if let r = o as? String {
return Double(r)
}
return nil
}
...
}
setToRouded
将图片设为圆角是一个比较常见的操作,可以通过 extension
简单封装一下:
extension UIView {
func setToRounded(radius: CGFloat? = nil) {
let r = radius ?? min(self.frame.size.width, self.frame.size.height) / 2
self.layer.cornerRadius = r
self.clipsToBounds = true
}
}
如果指定半径就设置圆角半径,如果没有值则取高和宽的最小值,实现『半圆』效果。
QUESTION
这里有个问题没太理解。请看下面两段代码:
extension UITableView {
func dequeueCell1<T: UITableViewCell>(cell: T.Type) {
print(cell)
}
static func dequeueCell2<T: UITableViewCell>(cell: T.Type) {
print(cell)
}
}
唯一的区别就是一个是实例方法一个是静态方法,但是调用的时候:
UITableView().dequeueCell1(RankingCell)
UITableView.dequeueCell2(RankingCell.self)
静态方法的参数需要添加 .self
而实例方法不需要。=。=没太搞懂这是哪出,还望赐教!
参考文献:
- Swift in Practice