开发文章

Swift3.0高仿ios斗鱼界面

前言

过去的2016年是一个直播年,各大平台都相继接入了直播频道,电商,社交…更是火了一批做视频的,譬如喵播,映客,都斗鱼等直播平台。全民直播,一下子掀起了直播的技术潮,今天要聊聊如何实现一个iOS的直播app。
首先来看最终的效果:

111.gif

最近也是因为入门Swift不久,在网上找了一个项目就开始模仿,本项目用到的第三方库:
Alamofire
Kingfisher

Swift3.0的蝶变

swift3.0相对于2.x,渐渐的脱离了oc和c的风格,不管是从命名规范还是新能上都有了较大的提升,笔者认为应该是未来一个相对稳定的版本,而不是1.0和2.0时代的实验版本。相对于2.x,我们来看3.0或以后的3.x主要有哪些特性:
稳定二进制接口(ABI)
API大家都知道是应用程序接口 API只是提供函数签名,而ABI是系统和语言层面的 如果ABI稳定 意味着以后Swift版本更新升级 我们不需要再修改老版本 Swift 语言编译的库了。
弹性/韧性 解决易碎二进制接口问题
Fragile binary interface problem是面向对象编程语言的通病 如果在程序中引入了外部库 我们的的程序中使用并继承了该外部库中的类 如果外部库有改动 我们必须重新编译所有该类的继承树 而这类问题被称为脆弱的基类 (Fragile base class)
可移植性
这个对于高级语言是很重要的特性,这意味着Swift可被移植到其他平台上。
全面支持泛型特性
Swift 2.2已经很好的支持泛型 但是还不够完善,Swift 3.0开始 将全面支持泛型的所有特性。
新的API设计规范

Swift3.0 发布了新的语言设计规范 其中在Swift3.0中标准库和核心库将会遵循这个设计规范。规范地址
从函数参数中删除var关键字

复制内容到剪贴板
  1. func doSomethingWithVar(var i: Int) {  
  2.      i = 2 // This will NOT have an effect on the caller's Int that was passed, but i can be modified locally  
  3.   }  
  4.   
  5. func doSomethingWithInout(inout i: Int) {  
  6.        i = 2 // This will have an effect on the caller's Int that was passed.  
  7. }    
  8.   
  9. doSomethingWithVar(x)    
  10.  print(x) // 1  
  11.   
  12. doSomethingWithInout(&x)  
  13.  print(x) // 2  

删除var是因为var与inout会产生歧义和混乱。
为autoreleasepool添加错误处理
旧版autoreleasepool处理错误方式:

复制内容到剪贴板
  1. func doWork() throws -> Result {  
  2.    var result: Result? = nil  
  3.    var error: ErrorProtocol? = nil  
  4.    autoreleasepool {   
  5.           do {  
  6.             ... actual computation which hopefully assigns to result but might not ...  
  7.          } catch let e {  
  8.                        error = e  
  9.           }  
  10.      }   
  11.   
  12.     guard let result = result else {   
  13.               throw error!   
  14.       }   
  15.           return result!  
  16.   }  

Swift3.0 autoreleasepool 处理错误方式:

复制内容到剪贴板
  1. public func autoreleasepool<Result>(@noescape body: () throws -> Result) rethrows -> Result  
  2.   
  3.   func doWork() throws -> Result {  
  4.   
  5.      return try autoreleasepool  
  6.          {   
  7.                  ... actual computation which either returns or throws       ...           
  8.          }  
  9. }  

允许直接引用(Default, Private, Repeat)关键字成员
在Swift3.0之前我们引用default和repeat成员时 需要这样写:

复制内容到剪贴板
  1. let cell = UITableViewCell(style: .`default`, reuseIdentifier: nil)  
  2. particleSystem.imageSequenceAnimationMode = SCNParticleImageSequenceAnimationMode.`repeat`  

Swift3.0时 允许我们直接访问default repeat 关键字成员:

复制内容到剪贴板
  1. let cell = UITableViewCell(style: .default, reuseIdentifier: nil)  
  2. particleSystem.imageSequenceAnimationMode = SCNParticleImageSequenceAnimationMode.repeat  

将声明式@noescape和@autoclosure 改为类型属性

复制内容到剪贴板
  1. func f(@noescape fn : () -> ()) {} // declaration  attribute   
  2.   
  3. //新的语法  
  4. func f(fn : @noescape () -> ()) {} // type attribute.  
  5. func f2(a : @autoclosure () -> ()) {} // type attribute.  

重命名 Debug 标示符
Debug 标示符重命名后将会与#available #selector 关键字统一风格。

复制内容到剪贴板
  1. __FILE__ ->  #file  
  2. __LINE__ -> #line  
  3. __COLUMN__ -> #column  
  4. __FUNCTION__ -> #function  
  5. __DSO_HANDLE__ -> #dsohandle  

斗鱼部分代码分析

本app采用的是mvvm的开发架构,做到业务,数据,页面的真正分离,我们来看几个核心的类:
base

复制内容到剪贴板
  1. import UIKit    
  2.   
  3. private let kItemMargin : CGFloat = 10    
  4. private let kHeaderViewH : CGFloat = 50    
  5. private let NormalCellID = "NormalCellID"    
  6. private let HeaderViewID = "HeaderViewID"    
  7. let kNormalItemW = (kScreenW - 33 * kItemMargin) / 2    
  8. let kNormalItemH = kNormalItemW * 3 / 4    
  9. let kPrettyItemH = kNormalItemW * 5 / 4    
  10. let PrettyCellID = "PrettyCellID"    
  11.   
  12. class BaseAnchorVC: BaseVC {    
  13.   
  14.     //!表示用到的时候保证有值    
  15.     var baseVM : BaseVM!    
  16.   
  17.     lazy var collectionView : UICollectionView = {[unowned self] in    
  18.         let layout = UICollectionViewFlowLayout()    
  19.         layout.itemSize = CGSize(width: kNormalItemW, height: kNormalItemH)    
  20.         layout.minimumLineSpacing = 0    
  21.         layout.minimumInteritemSpacing = kItemMargin    
  22.         layout.headerReferenceSize = CGSize(width: kScreenW, height: kHeaderViewH)    
  23.         layout.sectionInset = UIEdgeInsets(top: 0, left: kItemMargin, bottom: 0, right: kItemMargin)    
  24.   
  25.         let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)    
  26.         collectionView.backgroundColor = UIColor.white    
  27.         collectionView.dataSource = self    
  28.         collectionView.delegate = self    
  29.         collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]    
  30.   
  31.         collectionView.register(UINib(nibName: "CollectionNormalCell", bundle: nil), forCellWithReuseIdentifier: NormalCellID)    
  32.         collectionView.register(UINib(nibName: "CollectionPrettyCell", bundle: nil), forCellWithReuseIdentifier: PrettyCellID)    
  33.         collectionView.register(UINib(nibName: "CollectionHeaderView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HeaderViewID)    
  34.   
  35.         return collectionView    
  36.         }()    
  37.   
  38.     override func viewDidLoad() {    
  39.         super.viewDidLoad()    
  40.         setupUI()    
  41.         loadData()    
  42.     }    
  43.   
  44. }    
  45.   
  46. extension BaseAnchorVC {    
  47.     override func setupUI() {    
  48.         contentView = collectionView    
  49.         view.addSubview(collectionView)    
  50.         super.setupUI()    
  51.     }    
  52. }    
  53.   
  54. extension BaseAnchorVC {    
  55.     func loadData() {    
  56.     }    
  57. }    
  58.   
  59. extension BaseAnchorVC : UICollectionViewDataSource {    
  60.     func numberOfSections(in collectionView: UICollectionView) -> Int {    
  61.         return baseVM.anchorGroups.count    
  62.     }    
  63.   
  64.     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {    
  65.         return baseVM.anchorGroups[section].anchors.count    
  66.     }    
  67.   
  68.     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {    
  69.         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NormalCellID, for: indexPath) as! CollectionNormalCell    
  70.         cell.anchor = baseVM.anchorGroups[indexPath.section].anchors[indexPath.item]    
  71.         return cell    
  72.     }    
  73.   
  74.     func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {    
  75.         let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: HeaderViewID, for: indexPath) as! CollectionHeaderView    
  76.         headerView.group = baseVM.anchorGroups[indexPath.section]    
  77.         return headerView    
  78.     }    
  79.   
  80. }    
  81.   
  82. extension BaseAnchorVC : UICollectionViewDelegate {    
  83.     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {    
  84.         let anchor = baseVM.anchorGroups[indexPath.section].anchors[indexPath.item]    
  85.         anchor.isVertical == 0 ? pushNormalRoomVc(anchor) : presentShowRoomVc(anchor)    
  86.     }    
  87.   
  88.     private func presentShowRoomVc(_ anchor : AnchorModel) {    
  89.         let showVc = ShowRoomVC()    
  90.         showVc.anchor = anchor    
  91.         present(showVc, animated: true, completion: nil)    
  92.     }    
  93.   
  94.     private func pushNormalRoomVc(_ anchor : AnchorModel) {    
  95.         let normalVc = NormalRoomVC()    
  96.         normalVc.anchor = anchor    
  97.         navigationController?.pushViewController(normalVc, animated: true)    
  98.     }    
  99. }   
复制内容到剪贴板
  1. import UIKit    
  2.   
  3. class GameVC: BaseAnchorVC {    
  4.     fileprivate lazy var gameVM : GameVM = GameVM()    
  5.     fileprivate lazy var menuView : MenuView = {    
  6.         let menuView = MenuView.menuView()    
  7.         menuView.frame = CGRect(x: 0, y: -kMenuViewH, width: kScreenW, height: kMenuViewH)//设置collectionView的-y,放置menuView    
  8.         return menuView    
  9.     }()    
  10.   
  11. }    
  12.   
  13. extension GameVC {    
  14.     override func setupUI() {    
  15.         super.setupUI()    
  16.         collectionView.addSubview(menuView)    
  17.         collectionView.contentInset = UIEdgeInsets(top: kMenuViewH, left: 0, bottom: 0, right: 0)//设置内边距    
  18.     }    
  19. }    
  20.   
  21. extension GameVC{    
  22.     override func loadData() {    
  23.         baseVM = self.gameVM    
  24.         gameVM.requestData {    
  25.             self.collectionView.reloadData()    
  26.             var gameGroups = Array(self.gameVM.anchorGroups[1...15])//0...15 & gameGroups.removeFirst()    
  27.             let moreGroup = AnchorGroup()    
  28.             moreGroup.tag_name = "更多分类"    
  29.             gameGroups.append(moreGroup)    
  30.             self.menuView.groups = gameGroups    
  31.             self.loadDataFinished()    
  32.         }    
  33.     }    
  34. }    

 

请求类:

复制内容到剪贴板
  1. import UIKit    
  2. import Alamofire    
  3.   
  4. enum MethodType {    
  5.     case get    
  6.     case post    
  7. }    
  8.   
  9. class HttpTools {    
  10.     class func requestData(_ type : MethodType, URLString : String, parameters : [String : Any]? = nil, finishedCallback :  @escaping (_ result : Any) -> ()) {    
  11.         let method = type == .get ? HTTPMethod.get : HTTPMethod.post    
  12.         Alamofire.request(URLString, method: method, parameters: parameters).responseJSON { (response) in    
  13.             guard let result = response.result.value else {    
  14.                 print(response.result.error)    
  15.                 return    
  16.             }    
  17.             finishedCallback(result)    
  18.         }    
  19.     }    
  20. }   

附:swift斗鱼app界面https://github.com/xiangzhihong/douyu
斗鱼完整代码oc https://pan.baidu.com/s/1nv8iubJ?errno=0&errmsg=Auth%20Login%20Sucess&&bduss=&ssnerror=0
oc代码原文 http://www.jianshu.com/p/4d2032ca9cc5

 

感谢 code_xzh 支持 磐实编程网 原文地址:
blog.csdn.net/xiangzhihong8/article/details/54706266

文章信息

发布时间:2017-02-01

作者:code_xzh

发布者:aquwcw

浏览次数: