分享一些自己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 }
当然这个方案还有优化的空间,比如将Row 的cellidentifier 属性用枚举来定义,判断和初始化的时候就不用操作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中的三巨头 ,灵活运用它们可以产生很多有趣的东西
未完,待续…