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

将声明式@noescape和@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代码原文

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

高仿ios斗鱼界面

前言 过去的2016年是一个直播年,各大平台都相继接入了直播频道,电商,社交…更是火了一批做视频的,譬如喵播,映客,都斗鱼等直播平台。全民直播,一下子掀起了直播...

20450
来自专栏Kurt Niu 的博客

ThoughtWorks 百年不变的面试题之 --- Merchant's Guide To The Galaxy

Code: https://github.com/zifeiniu/Merchants-Guide-To-The-Galaxy

1.5K10
来自专栏码神联盟

红包 | 教你使用 JAVA 抢红包,次次都是运气王

39630
来自专栏行者悟空

利用Hadoop Mapreduce实现pv统计分析

33930
来自专栏有刻

Java 小记 - 时间的处理与探究

时间的处理与日期的格式转换几乎是所有应用的基础职能之一,几乎所有的语言都会为其提供基础类库。作为曾经 .NET 的重度使用者,赖其优雅的语法,特别是可扩展方法这...

18850
来自专栏码农阿宇

NopCommerce开源项目中很基础但是很实用的C# Helper方法

刚过了个五一,在杭州到处看房子,不知道杭州最近怎么了,杭州买房的人这么多,房价涨得太厉害,这几年翻倍翻倍地涨,刚过G20,又要亚运会,让我这样的刚需用户买不起,...

429110
来自专栏HansBug's Lab

3391: [Usaco2004 Dec]Tree Cutting网络破坏

3391: [Usaco2004 Dec]Tree Cutting网络破坏 Time Limit: 1 Sec  Memory Limit: 128 MB Su...

336100
来自专栏工科狗和生物喵

【计算机本科补全计划】CCF计算机职业资格认证 201709-01/02详解

正文之前 貌似我找到的那个题目网站更新了一波最新的题目。不过201709 只有1、2 题,所以先做了吧(其实是我自己对能不能做出第三题 持怀疑态度!)宝宝心里苦...

32260
来自专栏Java帮帮-微信公众号-技术文章全总结

秒懂,Java 注解 (Annotation)你可以这样学【面试+工作】

这处图片引自老罗的博客。为了避免不必要的麻烦,首先声明我个人比较尊敬老罗的。至于为什么放这张图,自然是为本篇博文服务,接下来我自会说明。好了,可以开始今天的博...

40830
来自专栏写代码的海盗

乐呵乐呵得了 golang入坑系列

开场就有料,今天返回去看了看以前的文章,轻松指数有点下降趋势。一琢磨,这不是我的风格呀。一反思,合着是这段时间,脑子里杂七杂八的杂事有点多,事情一多,就忘了快乐...

35950

扫码关注云+社区

领取腾讯云代金券