高仿ios斗鱼界面详解手机开发

前言

过去的2016年是一个直播年,各大平台都相继接入了直播频道,电商,社交…更是火了一批做视频的,譬如喵播,映客,都斗鱼等直播平台。全民直播,一下子掀起了直播的技术潮,今天要聊聊如何实现一个ios的直播app。
首先来看最终的效果:
这里写图片描述
最近也是因为入门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关键字

func doSomethingWithVar(var i: Int) { 
     i = 2 // This will NOT have an effect on the caller's Int that was passed, but i can be modified locally 
  } 
 
func doSomethingWithInout(inout i: Int) { 
       i = 2 // This will have an effect on the caller's Int that was passed. 
}   
 
doSomethingWithVar(x)   
 print(x) // 1 
 
doSomethingWithInout(&x) 
 print(x) // 2

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

func doWork() throws -> Result { 
   var result: Result? = nil 
   var error: ErrorProtocol? = nil 
   autoreleasepool {  
          do { 
            ... actual computation which hopefully assigns to result but might not ... 
         } catch let e { 
                       error = e 
          } 
     }  
 
    guard let result = result else {  
              throw error!  
      }  
          return result! 
  }

Swift3.0 autoreleasepool 处理错误方式:

public func autoreleasepool<Result>(@noescape body: () throws -> Result) rethrows -> Result 
 
  func doWork() throws -> Result { 
 
     return try autoreleasepool 
         {  
                 ... actual computation which either returns or throws       ...          
         } 
}

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

let cell = UITableViewCell(style: .`default`, reuseIdentifier: nil) 
particleSystem.imageSequenceAnimationMode = SCNParticleImageSequenceAnimationMode.`repeat`

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

let cell = UITableViewCell(style: .default, reuseIdentifier: nil) 
particleSystem.imageSequenceAnimationMode = SCNParticleImageSequenceAnimationMode.repeat

[email protected]@autoclosure 改为类型属性

func f(@noescape fn : () -> ()) {} // declaration  attribute  
 
//新的语法 
func f(fn : @noescape () -> ()) {} // type attribute. 
func f2(a : @autoclosure () -> ()) {} // type attribute.

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

__FILE__ ->  #file 
__LINE__ -> #line 
__COLUMN__ -> #column 
__FUNCTION__ -> #function 
__DSO_HANDLE__ -> #dsohandle

斗鱼部分代码分析

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

import UIKit   
 
private let kItemMargin : CGFloat = 10   
private let kHeaderViewH : CGFloat = 50   
private let NormalCellID = "NormalCellID"   
private let HeaderViewID = "HeaderViewID"   
let kNormalItemW = (kScreenW - 33 * kItemMargin) / 2   
let kNormalItemH = kNormalItemW * 3 / 4   
let kPrettyItemH = kNormalItemW * 5 / 4   
let PrettyCellID = "PrettyCellID"   
 
class BaseAnchorVC: BaseVC {   
 
    //!表示用到的时候保证有值   
    var baseVM : BaseVM!   
 
    lazy var collectionView : UICollectionView = {[unowned self] in   
        let layout = UICollectionViewFlowLayout()   
        layout.itemSize = CGSize(width: kNormalItemW, height: kNormalItemH)   
        layout.minimumLineSpacing = 0   
        layout.minimumInteritemSpacing = kItemMargin   
        layout.headerReferenceSize = CGSize(width: kScreenW, height: kHeaderViewH)   
        layout.sectionInset = UIEdgeInsets(top: 0, left: kItemMargin, bottom: 0, right: kItemMargin)   
 
        let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)   
        collectionView.backgroundColor = UIColor.white   
        collectionView.dataSource = self   
        collectionView.delegate = self   
        collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]   
 
        collectionView.register(UINib(nibName: "CollectionNormalCell", bundle: nil), forCellWithReuseIdentifier: NormalCellID)   
        collectionView.register(UINib(nibName: "CollectionPrettyCell", bundle: nil), forCellWithReuseIdentifier: PrettyCellID)   
        collectionView.register(UINib(nibName: "CollectionHeaderView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HeaderViewID)   
 
        return collectionView   
        }()   
 
    override func viewDidLoad() {   
        super.viewDidLoad()   
        setupUI()   
        loadData()   
    }   
 
}   
 
extension BaseAnchorVC {   
    override func setupUI() {   
        contentView = collectionView   
        view.addSubview(collectionView)   
        super.setupUI()   
    }   
}   
 
extension BaseAnchorVC {   
    func loadData() {   
    }   
}   
 
extension BaseAnchorVC : UICollectionViewDataSource {   
    func numberOfSections(in collectionView: UICollectionView) -> Int {   
        return baseVM.anchorGroups.count   
    }   
 
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {   
        return baseVM.anchorGroups[section].anchors.count   
    }   
 
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {   
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NormalCellID, for: indexPath) as! CollectionNormalCell   
        cell.anchor = baseVM.anchorGroups[indexPath.section].anchors[indexPath.item]   
        return cell   
    }   
 
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {   
        let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: HeaderViewID, for: indexPath) as! CollectionHeaderView   
        headerView.group = baseVM.anchorGroups[indexPath.section]   
        return headerView   
    }   
 
}   
 
extension BaseAnchorVC : UICollectionViewDelegate {   
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {   
        let anchor = baseVM.anchorGroups[indexPath.section].anchors[indexPath.item]   
        anchor.isVertical == 0 ? pushNormalRoomVc(anchor) : presentShowRoomVc(anchor)   
    }   
 
    private func presentShowRoomVc(_ anchor : AnchorModel) {   
        let showVc = ShowRoomVC()   
        showVc.anchor = anchor   
        present(showVc, animated: true, completion: nil)   
    }   
 
    private func pushNormalRoomVc(_ anchor : AnchorModel) {   
        let normalVc = NormalRoomVC()   
        normalVc.anchor = anchor   
        navigationController?.pushViewController(normalVc, animated: true)   
    }   
} 
import UIKit   
 
class GameVC: BaseAnchorVC {   
    fileprivate lazy var gameVM : GameVM = GameVM()   
    fileprivate lazy var menuView : MenuView = {   
        let menuView = MenuView.menuView()   
        menuView.frame = CGRect(x: 0, y: -kMenuViewH, width: kScreenW, height: kMenuViewH)//设置collectionView的-y,放置menuView   
        return menuView   
    }()   
 
}   
 
extension GameVC {   
    override func setupUI() {   
        super.setupUI()   
        collectionView.addSubview(menuView)   
        collectionView.contentInset = UIEdgeInsets(top: kMenuViewH, left: 0, bottom: 0, right: 0)//设置内边距   
    }   
}   
 
extension GameVC{   
    override func loadData() {   
        baseVM = self.gameVM   
        gameVM.requestData {   
            self.collectionView.reloadData()   
            var gameGroups = Array(self.gameVM.anchorGroups[1...15])//0...15 & gameGroups.removeFirst()   
            let moreGroup = AnchorGroup()   
            moreGroup.tag_name = "更多分类"   
            gameGroups.append(moreGroup)   
            self.menuView.groups = gameGroups   
            self.loadDataFinished()   
        }   
    }   
}  

请求类:

import UIKit   
import Alamofire   
 
enum MethodType {   
    case get   
    case post   
}   
 
class HttpTools {   
    class func requestData(_ type : MethodType, URLString : String, parameters : [String : Any]? = nil, finishedCallback :  @escaping (_ result : Any) -> ()) {   
        let method = type == .get ? HTTPMethod.get : HTTPMethod.post   
        Alamofire.request(URLString, method: method, parameters: parameters).responseJSON { (response) in   
            guard let result = response.result.value else {   
                print(response.result.error)   
                return   
            }   
            finishedCallback(result)   
        }   
    }   
} 

附:swift斗鱼app界面
斗鱼完整代码oc
oc代码原文

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/5953.html

(0)
上一篇 2021年7月17日
下一篇 2021年7月17日

相关推荐

发表回复

登录后才能评论