JFDouYu-Swift

該demo是看
iOS-Swift開發(fā)項目-斗魚直播APP 的視頻后重寫了一下
地址:https://www.bilibili.com/video/BV1qJ411B7G3?p=69
注:本文適合有一定的OC基礎(chǔ),Swift新手閱讀

好了 斗魚直播的 Swift版本上圖


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

接下里分析從首頁開始分析:
自定義導(dǎo)航欄:


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 建議構(gòu)造函數(shù)
     1沼瘫、構(gòu)造函數(shù)不需要寫返回值
     2买乃、在extension 只能擴充便利構(gòu)造函數(shù) convenience 加上開頭便利構(gòu)造函數(shù)
     3、必須 明確調(diào)用一個設(shè)計的構(gòu)造函數(shù)(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 {
            //按鈕大小自適應(yīng)
            btn.sizeToFit()
        }else{
            btn.frame = CGRect(origin: .zero, size:size)
        }
        
        self.init(customView:btn)
    }
    
   
}

自定定頂部滾動條


image.png
class JFPageTitleView: UIView {
    
    private var titles:[String]
    //懶加載一個數(shù)組
    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沒有隱式轉(zhuǎn)化的  一個是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"

//標明只能被類遵守  (如果不寫也可以被 結(jié)構(gòu)體,枚舉遵守 不建議)這樣不能把代理屬性定義為可選類型
//定義一個協(xié)議
protocol JFPageContentViewDelegate : class {
    //聲明一個協(xié)議的方法
    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
    }()

   
    //構(gòu)造函數(shù)改成可選類型
    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 {
            //如果是可選類型那么就是 要用可選鏈來調(diào)用
            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會循環(huán)引用 先移除
        for view in cell.contentView.subviews {
            view.removeFromSuperview()
        }
        
        //給cell設(shè)置內(nèi)容
        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) {
        
        //禁止執(zhí)行scroll的代理
        isForbidScrollDelegate = true
        
        let offSetX = CGFloat(currentIndex) * collectionView.frame.width
        //設(shè)置collectionview的偏移量
        collectionView.setContentOffset(CGPoint(x: offSetX, y: 0), animated: false)
    }
}

網(wǎng)絡(luò)請求:

import Alamofire

enum MethodType {
    case GET
    case POST
}

class JFNetworkTool{
    
    //字典類型 [String:NSString]
    //parameters:[String:NSString]? = nil, 默認參數(shù) 方便外面調(diào)用
    //callBack:()->() 閉包的寫法 第一個()里面添加參數(shù)
    
    //逃逸閉包 @escaping 閉包在另外一個閉包中使用需要  @escaping修飾
    //result:AnyObject 改成any 則callBack(result as AnyObject) 改成            callBack(result)
    /*
     parameters: parameters,
      encoding: URLEncoding.default,
      headers: nil).responseJSON { (response) in
     有默認參數(shù) 可以直接去掉
     */
    
    

    
    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字典轉(zhuǎn)模型


/// 子類可以繼承父類的所有屬性和方法 可以用來抽取 公共的屬性和方法
class BaseGameModel: NSObject {
    
    //定義屬性  swift 4.0 之后需要手動添加@objc 否則轉(zhuǎn)模型會沒有值
        @objc var tag_name : String = ""
        @objc var icon_url : String = ""

        init(dict:[String:Any]) {
           super.init()
           setValuesForKeys(dict)
        }
        //構(gòu)造函數(shù) 在調(diào)用的時候 才可以用 AnchorGroup()來創(chuàng)建
        override init() {}

        override func setValue(_ value: Any?, forUndefinedKey key: String) {}

}
   override func setValue(_ value: Any?, forUndefinedKey key: String) {}

這個一定要重寫 要不然 字段超出要解析的字段會奔潰

歸納下Swift一些常見且高頻注意的點:

類型推導(dǎo):
image.png
基本運算:
image.png
image.png
邏輯分支Guard:
image.png
for 循環(huán):
image.png

image.png
元祖
image.png
image.png
可選類型

舉例兩種錯誤寫法:


image.png
image.png
image.png
image.png
image.png

image.png
類型轉(zhuǎn)換
image.png
image.png
image.png

本期就總結(jié)到這 下期繼續(xù) 并上源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吮龄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咆疗,老刑警劉巖漓帚,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異午磁,居然都是意外死亡尝抖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門迅皇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昧辽,“玉大人,你說我怎么就攤上這事登颓〗淋瘢” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咕痛。 經(jīng)常有香客問我痢甘,道長,這世上最難降的妖魔是什么茉贡? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任塞栅,我火速辦了婚禮,結(jié)果婚禮上腔丧,老公的妹妹穿的比我還像新娘放椰。我一直安慰自己,他們只是感情好愉粤,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布砾医。 她就那樣靜靜地躺著,像睡著了一般衣厘。 火紅的嫁衣襯著肌膚如雪藻烤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天头滔,我揣著相機與錄音怖亭,去河邊找鬼。 笑死坤检,一個胖子當著我的面吹牛兴猩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播早歇,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼倾芝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了箭跳?” 一聲冷哼從身側(cè)響起晨另,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谱姓,沒想到半個月后借尿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡屉来,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年路翻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茄靠。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡茂契,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慨绳,到底是詐尸還是另有隱情掉冶,我是刑警寧澤真竖,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站厌小,受9級特大地震影響恢共,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜召锈,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一旁振、第九天 我趴在偏房一處隱蔽的房頂上張望获询。 院中可真熱鬧涨岁,春花似錦、人聲如沸吉嚣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尝哆。三九已至秉撇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秋泄,已是汗流浹背琐馆。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留恒序,地道東北人瘦麸。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像歧胁,于是被迫代替她去往敵國和親滋饲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353