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: } }
|
但是在调用 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
而实例方法不需要。=。=没太搞懂这是哪出,还望赐教!
参考文献: