swift 项目实战演练

发现

  • 偶然间发现一个很好的手把手教学项目,swift直播教学技术,在这里奉献出来,都是很实用的项目实战技术,拿来练手学习非常棒
    网易云课堂

共同进步,爱生活,爱钻研,爱分享!

项目实战核心技术分享

注意: *本文不是简单的代码练习,而是在原项目实战的基础上添加流行的框架尤其是swift中让人头疼的网络解析 模型转换 *

  • 项目中使用的第三方框架

pod 'Alamofire'
pod 'SwiftyJSON'
pod 'Kingfisher'

  • 项目技术点
  1. UIcollectionView 的高级用法,
  2. 类似今日头条的横向菜单
  3. 无限轮播图复用实现
  4. xib 快速布局
  5. Alamofire 组建封装
  6. 三方库的使用
  7. MVVM 思想运用
  8. 算法小技巧

项目效果

QQ20180313-140323.gif

开启代码之旅

横向菜单原理

  1. 标题部分PageTitleView 自定义UIview
代码请到github 中下载  https://github.com/zyjian/DouYuZB/
  • 这样写是不是很清爽,给类不断的扩展方法让我们的代码结构化方便管理


    50BB7174-A400-4B02-9A1C-B1B16B4BB4B3.png
  1. 下面的content 也是一个自定义的UIView 的子类 PageContentView,上面放了一个UIcollectionView,这是横线滚动
  2. 让PageTitleView 与 PageContentView建立对一个逻辑关系
    这里有点需要注意的就是,慢慢滑动的时候 标题的颜色从orange 渐变到 gray
// MARK: - 对外暴露方法
extension PageTitleView {
    func setCurrentIndex(sourceIndex: Int, targetIndex: Int, process: CGFloat) {
        //1.取出sourceLabel targetLabel
        let sourceLabel: UILabel = titleLabels[sourceIndex]
        let targetLabel: UILabel = titleLabels[targetIndex]
        
        
        //2.改变下标值
        currentIndex = targetIndex
        
        //3.移动滑块逻辑
        let moveTotalX = targetLabel.frame.origin.x - sourceLabel.frame.origin.x
        let moveX = moveTotalX * process
        scrollLine.frame.origin.x = sourceLabel.frame.origin.x + moveX
        
        //4.颜色渐变33
        //4.1取出颜色变化范围
        let colorDelta = (kSelectedColor.0 - kNormalColor.0 , kSelectedColor.1 - kNormalColor.1 , kSelectedColor.2 - kNormalColor.2)
        //4.2变化sourceLabel
        sourceLabel.textColor = UIColor.init(r: kSelectedColor.0 - colorDelta.0*process, g: kSelectedColor.1 - colorDelta.1*process, b: kSelectedColor.2 - colorDelta.2 * process, a: 1)
        //4.3变化sourceLabel
        targetLabel.textColor = UIColor.init(r: kNormalColor.0 + colorDelta.0*process, g:kNormalColor.1 + colorDelta.1*process , b: kNormalColor.2 + colorDelta.2*process, a: 1)
        
    }
} 

PageContentView 监听collectionView滑动 这里有个小算法这样更简单

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if isForbidScrollDelegate { return }
        
        let nowOffsetX : CGFloat = scrollView.contentOffset.x
        
        
        var soureceIndex : Int
        var targetIndex :Int
        var process : CGFloat
        //左滑
        if nowOffsetX > beganOffsetX {
            
            //1.确定process
            process = (nowOffsetX/scrollView.frame.width - CGFloat((floor(nowOffsetX/scrollView.frame.width))))

            
            //2. 确定 soureceIndex,
            soureceIndex = Int(nowOffsetX/scrollView.frame.width)
            
            //3.确定  targetIndex
            targetIndex = soureceIndex + 1
            if targetIndex >= childVcs.count {
                targetIndex = childVcs.count - 1
            }
            
            //4.完全划过去
            if nowOffsetX - beganOffsetX == scrollView.frame.width {
                process = 1
                targetIndex = soureceIndex
            }
           
            
        }else{
            //右滑
            
            //1.确定process
            process = 1 - (nowOffsetX/scrollView.frame.width - CGFloat((floor(nowOffsetX/scrollView.frame.width))))

            //2.确定  targetIndex
            targetIndex = Int(nowOffsetX/scrollView.frame.width)
            
            //3. 确定 soureceIndex,
            soureceIndex = targetIndex + 1
            
            //4.完全划过去
            if soureceIndex >= childVcs.count {
                process = 1
                soureceIndex = targetIndex
            }
            
            
            
        }
        Log("soureceIndex:\(soureceIndex) ,targetIndex:\(targetIndex) process:\(process)")
        delegate?.pageContentView(source: soureceIndex, target: targetIndex, process: process)
    }

网络请求工具类封装

import UIKit
import Alamofire

enum MethodType {
    case get
    case post
}

extension Dictionary {
    func dic2urlString() -> String {
        let muStr:NSMutableString = NSMutableString()
        
        for (k,v) in self {
            let temp : String = "\(k)=\(v)&"
            muStr.append(temp)        }
        
        return String(muStr.substring(to: muStr.length-1))
    }
}

class NetworkTools {
    class func requestData(type :MethodType, URLString: String , parameters: [String : Any]? = nil ,finishedCallback:@escaping ( _ result : AnyObject) -> ()) {
        
        //1.获取类型
        let method = type == .get ? HTTPMethod.get : HTTPMethod.post
        
        // 请求头
        var headers: [String : String]? {
            return ["Content-type" : "application/json"]
        }
        //2.发送网络请求
        Alamofire.request(URLString, method: method, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON { (response) in
            
            //3.获取结果
            guard let result = response.result.value else {
                Log("请求错误\(String(describing: response.result.error))")
                return
            }
            
            if parameters != nil {
                Log("\(URLString)?\n\(parameters!.dic2urlString()) \n \(String(describing: response.result.value))")
            }
            //4.将结果返回
            finishedCallback(result as AnyObject)
        }
    }
}

// 扩展方法
private extension String {
    var utf8Encoded: Data {
        return data(using: .utf8)!
    }
}

使用方法

 //3.请求第一部分推荐数据
        NetworkTools.requestData(type: .get, URLString: "http://capi.douyucdn.cn/api/v1/getbigDataRoom", parameters: ["time" : Date.getCurrentTime()]) { (result) in
            let json = JSON(result)
            let data = json["data"].arrayValue

            for dic in data{
                let model = RoomModel.init(jsonData: dic)
                self.bigDataGroup.room_list.append(model)
            }
            self.bigDataGroup.tag_name = "热门"
            self.bigDataGroup.icon_name = "home_header_hot"

        }

SwiftyJSON 的使用 model 类构建

import UIKit
import SwiftyJSON

struct RoomModel {
    var specific_catalog:String?
    var vertical_src:String?
    var nickname :String?
    var game_name:String?
    var room_name:String?
    var anchor_city:String?
    var icon_url:String?
    var tag_name:String?

    var room_id:Int?
    var show_time:Int?
    var isVertical:Int?
    var owner_uid:Int?
    var online:Int?
    
    
    init() {
        
    }
    init(jsonData:JSON) {
        specific_catalog = jsonData["specific_catalog"].stringValue
        vertical_src = jsonData["vertical_src"].stringValue
        nickname = jsonData["nickname"].stringValue
        game_name = jsonData["game_name"].stringValue
        room_name = jsonData["room_name"].stringValue
        anchor_city = jsonData["anchor_city"].stringValue
        icon_url = jsonData["icon_url"].stringValue
        tag_name = jsonData["tag_name"].stringValue

        
        online = jsonData["online"].intValue
        room_id = jsonData["room_id"].intValue
        show_time = jsonData["show_time"].intValue
        isVertical = jsonData["isVertical"].intValue
        owner_uid = jsonData["owner_uid"].intValue

    }
}

什么是MVVM ?

87E7B604-2232-4C8F-BE4B-CF54A8BDA4C7.jpeg

看明白了吗?看不懂没关系直接上代码


26C8049F-FB42-4DD0-AD90-D6F93C540512.png

MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。modelView 它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。本项目中的Viewmodel 负责处理 网络请求的,这样我们的控制器中更加简单。

是不是很简单呢

更多代码详情技巧 请到 Demo 中查看,欢迎留下足迹,共同进步!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,943评论 4 60
  • 正好我市有了滑雪场,在朋友的呼唤下跑去体验人生,改善目前生活压抑的状态。 到滑雪场时天气很冷,但是心情也是激动,毕...
    傲七阅读 163评论 0 0
  • 莫名其妙 抓不到了 摸不着了 猫说 只不过打了个哈欠 还没陪个够 怎么就断了
    彭小彭阅读 155评论 0 1
  • 最悲哀的爱情莫过于 一个懂爱的人遇到了一个不懂爱的人 然后经历一场撕心裂肺的爱情后分开 后来不懂爱的人慢慢懂了 懂...
    沙野稚阅读 165评论 0 0