发现
- 偶然间发现一个很好的手把手教学项目,swift直播教学技术,在这里奉献出来,都是很实用的项目实战技术,拿来练手学习非常棒
网易云课堂
共同进步,爱生活,爱钻研,爱分享!
项目实战核心技术分享
注意: *本文不是简单的代码练习,而是在原项目实战的基础上添加流行的框架尤其是swift中让人头疼的网络解析 模型转换 *
- 项目中使用的第三方框架
pod 'Alamofire'
pod 'SwiftyJSON'
pod 'Kingfisher'
- 项目技术点
- UIcollectionView 的高级用法,
- 类似今日头条的横向菜单
- 无限轮播图复用实现
- xib 快速布局
- Alamofire 组建封装
- 三方库的使用
- MVVM 思想运用
- 算法小技巧
项目效果
开启代码之旅
横向菜单原理
- 标题部分PageTitleView 自定义UIview
代码请到github 中下载 https://github.com/zyjian/DouYuZB/
-
这样写是不是很清爽,给类不断的扩展方法让我们的代码结构化方便管理
- 下面的content 也是一个自定义的UIView 的子类 PageContentView,上面放了一个UIcollectionView,这是横线滚动
- 让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 ?
看明白了吗?看不懂没关系直接上代码
MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。modelView 它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。本项目中的Viewmodel 负责处理 网络请求的,这样我们的控制器中更加简单。
是不是很简单呢
更多代码详情技巧 请到 Demo 中查看,欢迎留下足迹,共同进步!