JFDouYu-Swift

作者: 土鳖不土 | 来源:发表于2020-09-19 14:46 被阅读0次

    该demo是看
    iOS-Swift开发项目-斗鱼直播APP 的视频后重写了一下
    地址:https://www.bilibili.com/video/BV1qJ411B7G3?p=69
    注:本文适合有一定的OC基础,Swift新手阅读

    好了 斗鱼直播的 Swift版本上图


    1.gif 2.gif 3.gif 4.gif

    接下里分析从首页开始分析:
    自定义导航栏:


    image.png
      private func setNavigationBar(){
            
            navigationItem.leftBarButtonItem = UIBarButtonItem(imageName: "logo")
            let size = CGSize(width: 40, height: 40)
            let historyItem = UIBarButtonItem(imageName: "image_my_history", higtImageName: "Image_my_history_click", size: size)
            let searchItem = UIBarButtonItem(imageName: "btn_search", higtImageName: "btn_search_clicked", size: size)
            let qrcodeItem = UIBarButtonItem(imageName: "Image_scan", higtImageName: "Image_scan_click", size: size)
            navigationItem.rightBarButtonItems = [historyItem,searchItem,qrcodeItem]
    
        }
    
    
    extension UIBarButtonItem{
        
        /*
         不建议这么写
         class func createItem(imageName: String, higtImageName:String ,size:CGSize) -> UIBarButtonItem{
                let btn  = UIButton()
                btn.setImage(UIImage(named: imageName), for: .normal)
                btn.setImage(UIImage(named: higtImageName), for: .highlighted)
                btn.frame = CGRect(origin: .zero, size:size)
                return UIBarButtonItem(customView: btn)
            }
         */
        
        
        
        
        /*
         swift 建议构造函数
         1、构造函数不需要写返回值
         2、在extension 只能扩充便利构造函数 convenience 加上开头便利构造函数
         3、必须 明确调用一个设计的构造函数(self)
         
         */
        
        /*
         默认字符串
         higtImageName:String = ""
         
         */
        
        convenience init(imageName: String, higtImageName:String = "" ,size:CGSize = .zero) {
            let btn  = UIButton()
            btn.setImage(UIImage(named: imageName), for: .normal)
            
            if higtImageName != "" {
                btn.setImage(UIImage(named: higtImageName), for: .highlighted)
            }
            if size == .zero {
                //按钮大小自适应
                btn.sizeToFit()
            }else{
                btn.frame = CGRect(origin: .zero, size:size)
            }
            
            self.init(customView:btn)
        }
        
       
    }
    

    自定定顶部滚动条


    image.png
    class JFPageTitleView: UIView {
        
        private var titles:[String]
        //懒加载一个数组
        private lazy var titleLabels:[UILabel] = [UILabel]()
        private var currentIndex:Int = 0
        //声明一个代理的属性
        weak var delegate:JFPageTitleViewDelegate?
         
        private lazy var scrollView:UIScrollView = {
            let scrollView = UIScrollView()
            scrollView.showsHorizontalScrollIndicator = false
            scrollView.scrollsToTop = false
            scrollView.isPagingEnabled = false
            scrollView.bounces = false
            return scrollView
        }()
        
        private lazy var scrollLine:UIView = {
            let scrollLine = UIView()
            scrollLine.backgroundColor = UIColor(r: KSelectColor.0, g: KSelectColor.1, b: KSelectColor.2, a: 1)
            return scrollLine
        }()
        
        init(frame: CGRect, titles:[String]) {
            self.titles = titles
            super.init(frame: frame)
            setupUI()
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    extension JFPageTitleView{
        private func setupUI(){
            scrollView.frame = bounds        
            
            addSubview(scrollView)
            
            setupTitleLabel()
            
            setupBottomLineAndScrollLine()
        }
        
        private func setupTitleLabel(){
            //swift没有隐式转化的  一个是CGFloat 类型 一个是 int类型不能直接 乘除
             let labelW:CGFloat = frame.width / CGFloat(titles.count)
             let labelH:CGFloat = frame.height - KScrollLineH
             let labelY:CGFloat = 0
            
            for (index,title) in titles.enumerated() {
                let label = UILabel()
                label.text = title
                label.tag = index
                label.font = UIFont.systemFont(ofSize: 16)
                label.textColor = UIColor(r: KNormalColor.0, g: KNormalColor.1, b: KNormalColor.2, a: 1)
                label.textAlignment = .center
                let labelX:CGFloat = labelW * CGFloat(index)
                
                label.frame = CGRect(x: labelX, y: labelY, width: labelW, height: labelH)
                scrollView.addSubview(label)
                titleLabels.append(label)
                
                label.isUserInteractionEnabled = true
                let tapGes = UITapGestureRecognizer(target: self, action: #selector(self.labelClick(tapGes:)))
                label.addGestureRecognizer(tapGes)
                
            }
        }
        
        private func setupBottomLineAndScrollLine(){
            let bottomLine = UIView()
            let lineH:CGFloat = 0.5
            bottomLine.backgroundColor = UIColor(r: 234, g: 234, b: 234, a: 1)
            bottomLine.frame = CGRect(x: 0, y:frame.height - lineH, width: frame.width, height: lineH)
            addSubview(bottomLine)
            
    //        titleLabels.first 是可选类型 用 guard进行判断
            guard let firstlabel  = titleLabels.first  else { return}
            firstlabel.textColor = UIColor(r: KSelectColor.0, g: KSelectColor.1, b: KSelectColor.2, a: 1)
            
            scrollView.addSubview(scrollLine)
            scrollLine.frame = CGRect(x: firstlabel.frame.origin.x, y: frame.height - KScrollLineH, width:firstlabel.frame.width, height: KScrollLineH)
        }
        
    }
    
    
    extension JFPageTitleView {
        // label点击
        @objc private func labelClick(tapGes:UITapGestureRecognizer){
            //当前的label
            guard let currentLabel = tapGes.view as? UILabel else {return}
            
            if currentLabel.tag == currentIndex {return}
    
            //old label
            let oldLabel = titleLabels[currentIndex]
            
            currentLabel.textColor = UIColor(r: KSelectColor.0, g: KSelectColor.1, b: KSelectColor.2, a: 1)
            oldLabel.textColor = UIColor(r: KNormalColor.0, g: KNormalColor.1, b: KNormalColor.2, a: 1)
            
            //保存最新label的下标值
            currentIndex = currentLabel.tag
            
            let scrollLineX = CGFloat(currentLabel.tag) * scrollLine.frame.size.width
            UIView.animate(withDuration: 0.15) {
                self.scrollLine.frame.origin.x = scrollLineX
            }
            
            //通知代理
            //代理必须是可选的 因为外部可以不遵守这个代理 所以是weak 修饰, "?"
            delegate?.JFPageTitleViewSelectAtIndex(titleView: self, selectIndex: currentIndex)
                    
        }
    }
    
    //对外暴露方法
    extension JFPageTitleView{
        func setTitleViewWithProgress(progress:CGFloat,sourceIndex:Int,targartIndex:Int){
            
            //取出label
            let sourceLabel = titleLabels[sourceIndex]
            let targartLabel = titleLabels[targartIndex]
            
            //处理滑块的逻辑
            let moveTotalX = targartLabel.frame.origin.x -  sourceLabel.frame.origin.x
            let moveX = moveTotalX * progress
            scrollLine.frame.origin.x = sourceLabel.frame.origin.x + moveX
            
            //颜色的渐变
            //取出变化范围
            let colorDelta =  (KSelectColor.0 - KNormalColor.0,
                               KSelectColor.1 - KNormalColor.1,
                               KSelectColor.2 - KNormalColor.2)
            
            
            //sourceLabel 是右 高亮边灰色 色值有大变小的过程
            sourceLabel.textColor = UIColor(r: KSelectColor.0 - colorDelta.0 * progress,
                                            g: KSelectColor.1 - colorDelta.1 * progress,
                                            b: KSelectColor.2 - colorDelta.2 * progress,
                                            a: 1)
            
            //targartLabel 是右 高亮边灰色 色值有小变大的的过程
            targartLabel.textColor = UIColor(r: KNormalColor.0 + colorDelta.0 * progress,
            g: KNormalColor.1 + colorDelta.1 * progress,
            b: KNormalColor.2 + colorDelta.2 * progress,
            a: 1)
            
            //记录currentIndex
            currentIndex = targartIndex
            
            
        }
    }
    

    contentView


    image.png
    private let KCcontentCellID = "KCcontentCellID"
    
    //标明只能被类遵守  (如果不写也可以被 结构体,枚举遵守 不建议)这样不能把代理属性定义为可选类型
    //定义一个协议
    protocol JFPageContentViewDelegate : class {
        //声明一个协议的方法
        func JFPageContentViewScrollWith(pageContentView:JFPageContentView,progress:CGFloat, sourceIndex:Int,targetIndex:Int)
    }
    
    class JFPageContentView: UIView {
        
        weak var delegate:JFPageContentViewDelegate?
        
        private var childVcs:[UIViewController]
        
        private var startOffSetX:CGFloat = 0
        /*
         用weak修饰 是可选类型 用 “?”修饰
         'weak' variable should have optional type 'UIViewController?'
         */
        
        private var isForbidScrollDelegate:Bool = false
        
        private weak var parentViewController:UIViewController?
        
        private lazy var collectionView:UICollectionView = { [weak self] in
            let layout = UICollectionViewFlowLayout()
            
            /*
             weak self 是可选类型
             self.bounds.size self?.bounds.size 也是可选类型 但是 layout.itemSize 这个是确定类型
             (self?.bounds.size)!  “!”强制解包
             */
            layout.itemSize = (self?.bounds.size)!
            layout.minimumLineSpacing = 0
            layout.minimumInteritemSpacing = 0
            layout.scrollDirection = .horizontal
            
            let frame:CGRect = .zero
            let collectionView  = UICollectionView(frame: frame, collectionViewLayout: layout)
            collectionView.showsHorizontalScrollIndicator = false
            collectionView.isPagingEnabled = true
            collectionView.bounces = false
            collectionView.dataSource = self
            collectionView.delegate = self
            collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: KCcontentCellID)
            return collectionView
        }()
    
       
        //构造函数改成可选类型
        init(frame: CGRect,childVcs:[UIViewController],parentViewController:UIViewController?) {
            self.childVcs = childVcs
            //可选类型 赋值给可选类型 OK的
            self.parentViewController = parentViewController
            super.init(frame: frame)
            setupUI()
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    extension JFPageContentView{
        private func setupUI(){
            //将所有的子控制器加到父控制器中
            for childvc in childVcs {
                //如果是可选类型那么就是 要用可选链来调用
                parentViewController?.addChild(childvc)
            }
            addSubview(collectionView)
            collectionView.frame = bounds
        }
    }
    
    //遵守UICOllectionView 的datasource
    extension JFPageContentView:UICollectionViewDataSource{
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return childVcs.count
        }
        
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: KCcontentCellID, for: indexPath)
            
            //cell会循环引用 先移除
            for view in cell.contentView.subviews {
                view.removeFromSuperview()
            }
            
            //给cell设置内容
            let childVc = childVcs[indexPath.item]
            childVc.view.frame = cell.contentView.bounds
            cell.contentView.addSubview(childVc.view)
            return cell
            
        }
        
    }
    
    extension JFPageContentView:UICollectionViewDelegate{
        
        func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
            
            isForbidScrollDelegate = false
            
            startOffSetX =  scrollView.contentOffset.x
        }
        
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            
            if (isForbidScrollDelegate) {return}
            
            var progress:CGFloat = 0
            var sourceIndex:Int = 0
            var targetIndex:Int = 0
            
            //判断左滑还是右滑
            let currentOffSetX = scrollView.contentOffset.x
            let scrollViewW  = scrollView.bounds.width
            if startOffSetX > currentOffSetX { //左滑
                //计算progress
    //            floor 取整
                progress = currentOffSetX / scrollViewW - floor(currentOffSetX / scrollViewW)
                
                //计算当前的 sourceIndex
                sourceIndex = Int(currentOffSetX / scrollViewW)
                
                //计算target
                targetIndex = sourceIndex + 1
                
                if targetIndex >= childVcs.count {
                    targetIndex = childVcs.count - 1
                }
                
                //如果完全滑过去
                if currentOffSetX - startOffSetX ==  scrollViewW{
                    progress = 1
                    targetIndex = sourceIndex
                }
                
                
            }else{ //右滑
                progress = 1 - (currentOffSetX / scrollViewW - floor(currentOffSetX / scrollViewW))
                
                //计算target
                targetIndex = Int(currentOffSetX / scrollViewW)
                
                //计算当前的 sourceIndex
                sourceIndex = targetIndex + 1
                
                if sourceIndex >= childVcs.count {
                    sourceIndex = childVcs.count - 1
                }
            }
            
            delegate?.JFPageContentViewScrollWith(pageContentView: self, progress: progress, sourceIndex: sourceIndex, targetIndex: targetIndex)
    
            
            
        }
    }
    
    
    
    //对外暴露的方法
    extension JFPageContentView{
        func setCurrentIndex(currentIndex:Int) {
            
            //禁止执行scroll的代理
            isForbidScrollDelegate = true
            
            let offSetX = CGFloat(currentIndex) * collectionView.frame.width
            //设置collectionview的偏移量
            collectionView.setContentOffset(CGPoint(x: offSetX, y: 0), animated: false)
        }
    }
    

    网络请求:

    import Alamofire
    
    enum MethodType {
        case GET
        case POST
    }
    
    class JFNetworkTool{
        
        //字典类型 [String:NSString]
        //parameters:[String:NSString]? = nil, 默认参数 方便外面调用
        //callBack:()->() 闭包的写法 第一个()里面添加参数
        
        //逃逸闭包 @escaping 闭包在另外一个闭包中使用需要  @escaping修饰
        //result:AnyObject 改成any 则callBack(result as AnyObject) 改成            callBack(result)
        /*
         parameters: parameters,
          encoding: URLEncoding.default,
          headers: nil).responseJSON { (response) in
         有默认参数 可以直接去掉
         */
        
        
    
        
        class func requestData(type:MethodType , urlString:String,parameters:[String:NSString]? = nil, callBack:@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 as Any)
                    return}
                callBack(result)
            }
            
        }
    
    }
    

    KVC字典转模型

    
    /// 子类可以继承父类的所有属性和方法 可以用来抽取 公共的属性和方法
    class BaseGameModel: NSObject {
        
        //定义属性  swift 4.0 之后需要手动添加@objc 否则转模型会没有值
            @objc var tag_name : String = ""
            @objc var icon_url : String = ""
    
            init(dict:[String:Any]) {
               super.init()
               setValuesForKeys(dict)
            }
            //构造函数 在调用的时候 才可以用 AnchorGroup()来创建
            override init() {}
    
            override func setValue(_ value: Any?, forUndefinedKey key: String) {}
    
    }
    
       override func setValue(_ value: Any?, forUndefinedKey key: String) {}
    

    这个一定要重写 要不然 字段超出要解析的字段会奔溃

    归纳下Swift一些常见且高频注意的点:

    类型推导:
    image.png
    基本运算:
    image.png image.png
    逻辑分支Guard:
    image.png
    for 循环:
    image.png
    image.png
    元祖
    image.png image.png
    可选类型

    举例两种错误写法:


    image.png image.png image.png image.png image.png
    image.png
    类型转换
    image.png image.png image.png

    本期就总结到这 下期继续 并上源码地址

    相关文章

      网友评论

        本文标题:JFDouYu-Swift

        本文链接:https://www.haomeiwen.com/subject/tltwektx.html