RainedAllNight‘s Blog

Swift-Tips

字数统计: 1.4k阅读时长: 7 min
2018/07/19 Share

分享一些自己Swift项目中用到的tips(持续更新)

1.巧用系统协议

1
2
3
4
5
6
extension Optional where Wrapped == String {
public var nilIfEmpty: String? {
guard let value = self else { return nil }
return value.isEmpty ? nil : value
}
}

扩展了Optional协议,添加了nilIfEmpty属性,添加判断:当当前字符串为空时返回nil

举个🌰

1
2
3
4
5
普通青年的字符判断
guard let mobile = self.mobileTextField.text, !mobile.isEmpty else {
self.showErrorHUD("手机号码不能为空")
return
}

1
2
3
4
5
文艺青年的字符判断
guard let mobile = self.mobileTextField.text.nilIfEmpty else {
self.showErrorHUD("手机号码不能为空")
return
}

再如

1
2
3
4
5
6
7
8
extension URL: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
guard let url = URL(string: value) else {
preconditionFailure("url transform is failure")
}
self = url
}
}

通过扩展URL实现ExpressibleByStringLiteral协议,让url有了字面量赋值方式

举个🌰

1
2
let url = "https://www.baidu.com"
let request = URLRequest(url: url)

2.规范Segue跳转

如果你的项目使用的是StoryBoard来进行开发的话肯定对Segue跳转不会陌生,但是随之而来的问题就是为了区别不同的segue而存在的identifier会以字符串的形式存在于各处,一处修改后需要所有地方同步修改,不然就会导致crash。那么有没有一种规范化的声明和调用的方式呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protocol KYXSegueable {
associatedtype CustomSegueIdentifier: RawRepresentable
func performCustomSegue(_ segue: CustomSegueIdentifier, sender: Any?)
func customSegueIdentifier(forSegue segue: UIStoryboardSegue) -> CustomSegueIdentifier?
}

extension KYXSegueable where Self: UIViewController, CustomSegueIdentifier.RawValue == String {

func performCustomSegue(_ segue: CustomSegueIdentifier, sender: Any?) {
performSegue(withIdentifier: segue.rawValue, sender: sender)
}

func customSegueIdentifier(forSegue segue: UIStoryboardSegue) -> CustomSegueIdentifier? {
guard let identifier = segue.identifier, let customSegueIndentifier = CustomSegueIdentifier(rawValue: identifier) else {
return nil
// fatalError("Cannot get custom segue indetifier for segue: \(segue.identifier ?? "")")
}

return customSegueIndentifier
}
}

我们定义了一个KYXSegueable的协议,关联了一个RawRepresentable类型,并定义了两个和系统类似的方法用于跳转和获取identifier,有关RawRepresentable可以进入Api看一下,遵循它的对象具有rawValue的属性和功能,我们最常见的枚举就遵循这类协议

那么如何使用呢

1
2
3
4
5
6
7
8
9
10
11
12
class KYXHomeViewController: KYXBaseViewController, KYXSegueable {

enum CustomSegueIdentifier: String {
case loginSegue
case coinListSegue
case vehicleListSegue
case dataSwitchSegue
case addDeviceSegue
case tripSegue
}

......

控制器实现KYXSegueable协议,并声明其所需的遵循RawRepresentable协议的关联类型,没错,就是一组枚举了,我们可以在这边定义我们跳转的segue

调用

1
self.performCustomSegue(.coinListSegue, sender: nil)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let segueId = CustomSegueIdentifier(rawValue: segue.identifier ?? "") else {
return
}

switch segueId {
case .vehicleListSegue:
let vehicleListController = segue.destination as? KYXMyVehicleListViewController
vehicleListController?.originalNavBarColor = self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedStringKey.foregroundColor] as? UIColor

case .tripSegue:
let tripListController = segue.destination as? KYXTripListTableViewController
tripListController?.recentTrip = self.homeModel?.trip

default:
break
}
}

3.更好地使用tableView-组织Section & Row

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
open class KYXSection: NSObject {
open var rows: [KYXRow] = []
open var data: Any?

public convenience init(rows: [KYXRow]) {
self.init()
self.rows = rows
}
}

open class KYXRow: NSObject {
open var cellIdentifier: String = ""
open var data: Any?
open var rowHeight: CGFloat = UITableViewAutomaticDimension // 返回具体行高或UITableViewAutomaticDimension

public convenience init(_ cellIdentifier: String) {
self.init()
self.cellIdentifier = cellIdentifier
self.rowHeight = UITableViewAutomaticDimension
}

public convenience init(_ cellIdentifier: String, data: Any?) {
self.init()
self.cellIdentifier = cellIdentifier
self.data = data
self.rowHeight = UITableViewAutomaticDimension
}

public convenience init(cellIdentifier identifier: String, data: Any?, rowHeight: CGFloat) {
self.init()
self.cellIdentifier = identifier
self.data = data
self.rowHeight = rowHeight
}
}

如何使用

第一步,组织Section

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    private func configureSections(_ homeModel: HomeModel) {
self.sections.removeAll()
//热点新闻
if let news = homeModel.news, news.count > 0 {
let hotNewsSection = KYXSection(rows: [KYXRow(cellIdentifier: CellID.hotNews.rawValue, data: homeModel.news, rowHeight: 80)])
self.sections.append(hotNewsSection)
}

//驾驶得分
if let trip = homeModel.trip {
let driveGradeSection = KYXSection()
var subtitle = "近七天均值"
if let trip = homeModel.trip, !trip.hasData {
subtitle = "近七天暂无行程"
}
let driveHaederRow = KYXRow(cellIdentifier: CellID.hader.rawValue, data: ["title": "驾驶评分", "subtitle": subtitle], rowHeight: 38)
let gradeRow = KYXRow(cellIdentifier: CellID.driveGrade.rawValue, data: trip, rowHeight: 138)
driveGradeSection.rows.append(contentsOf: [driveHaederRow, gradeRow])
self.sections.append(driveGradeSection)
}

if let banners = homeModel.banners, banners.count > 0 {
let bannerSection = KYXSection()
let bannerRow = KYXRow(cellIdentifier: CellID.banner.rawValue, data: homeModel.banners, rowHeight: (self.view.bounds.width)/4)
bannerSection.rows.append(bannerRow)
self.sections.append(bannerSection)
}

if let ranks = homeModel.ranks, ranks.count > 0 {
let rankingSection = KYXSection()
let rankingHaederRow = KYXRow(cellIdentifier: CellID.hader.rawValue, data: ["title": "安全驾驶排名", "subtitle": "每周日24时结算"], rowHeight: 38)
let rankingRow = KYXRow(cellIdentifier: CellID.rank.rawValue, data: homeModel.ranks, rowHeight: 166)
rankingSection.rows.append(contentsOf: [rankingHaederRow, rankingRow])
self.sections.append(rankingSection)
}
}

..........

TableView代理方法

1
2
3
4
5
6
7
func numberOfSections(in tableView: UITableView) -> Int {
return self.sections.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.sections[section].rows.count
}

更优雅的Cell类型判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = self.sections[indexPath.section].rows[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: row.cellIdentifier, for: indexPath)
let data = row.data

switch row.cellIdentifier {
case CellID.banner.rawValue:
let bannerCell = cell as? HomeBannerTableViewCell
bannerCell?.configureCell(data as? [ServiceManageModel])

case CellID.section.rawValue:
cell.backgroundColor = UIColor.white

case CellID.funcList.rawValue:
let cell = cell as? HomeFunctionListTableViewCell
cell?.configureCell(services: data as? [HomeService], index: indexPath.row)

case CellID.driveGrade.rawValue:
let cell = cell as? HomeDriveGradeTableViewCell
cell?.configureCell(model: data as? HomeTrip)

case CellID.rank.rawValue:
let rankCell = cell as? HomeRankingTableViewCell
rankCell?.configureCell(models: data as? [HomeRank])

case CellID.hotNews.rawValue:
let newsCell = cell as? HomeHotNewsTableViewCell
newsCell?.configureCell(models: data as? [HomeNews])
default:
return cell
}

return cell
}

当然这个方案还有优化的空间,比如将Rowcellidentifier属性用枚举来定义,判断和初始化的时候就不用操作rawValue了


4.简洁声明多个变量

对于一些相互有关联的变量,相比于在每行中声明一个,还有一种更简洁美观的方式:

1
2
var (top, left, width, height) = (0.0, 0.0, 100.0, 50.0)
//rect.width = width

5.Notification的管理

类似于Segue, 也可以通过枚举来规范管理项目中的Notification

1
2
3
4
5
6
7
8
9
extension NotificationCenter {
static func post(name: KYXNotification, object: Any?, userInfo: [AnyHashable: Any]?) {
NotificationCenter.default.post(name: NSNotification.Name.init(name.rawValue), object: object, userInfo: userInfo)
}

static func addObserver(_ observer: Any, selector: Selector, name: KYXNotification, object: Any?) {
NotificationCenter.default.addObserver(observer, selector: selector, name: NSNotification.Name.init(name.rawValue), object: object)
}
}
1
2
3
4
5
6
7
定义所需的通知名

public enum KYXNotification: String {
case loginTimeout
case loginSuccess
case logoutSuccess
}

使用

1
NotificationCenter.post(name: .loginSuccess, object: nil, userInfo: nil)

1
NotificationCenter.addObserver(self, selector: #selector(didLoginSuccess), name: .loginSuccess, object: nil)

Protocol + Enum + Extension组成了Swift中的三巨头 ,灵活运用它们可以产生很多有趣的东西


未完,待续…

CATALOG
  1. 1. 分享一些自己Swift项目中用到的tips(持续更新)
    1. 1.1. 1.巧用系统协议
      1. 1.1.1. 再如
    2. 1.2. 2.规范Segue跳转
    3. 1.3. 3.更好地使用tableView-组织Section & Row
    4. 1.4. 4.简洁声明多个变量
    5. 1.5. 5.Notification的管理
    6. 1.6. 未完,待续…