再次写swift的收获

1.网络请求工具

import Moya
import CocoaLumberjack
import SwiftyJSON

/// 超时时长
private var requestTimeOut:Double = 30

// NetworkAPI就是一个遵循TargetType协议的枚举
let NetworkProvider = MoyaProvider(endpointClosure: myEndpointClosure, requestClosure: requestClosure, plugins: [networkPlugin], trackInflights: false)

/// 定义基础域名
let Moya_baseURL = "你的前缀域名"

/// 定义返回的JSON数据字段
let RESULT_CODE = "你的状态码字段名"      //状态码
let RESULT_MESSAGE = "你的错误消息提示字段名"  //错误消息提示

/// 相关API,就是当这个枚举是,会引入这两个参数
enum NetworkAPI {
    case list(pageNO: Int = 1, pageSize: Int = 10)
    case intelligentRecommendation(pageNo:Int = 1)
    case upload(bodyData: Data)
}

/// 实现TargetType协议
extension NetworkAPI:TargetType{
    /// 前缀URL
    public var baseURL: URL{
        return URL(string: Moya_baseURL)!
    }
    
    // 请求路径(后缀)
    var path: String {
        switch self {
        case .list:
            return "list"
        case .intelligentRecommendation(pageNo:_):
            return "请求的后缀参数"
        case .upload(bodyData: _):
            return ""
        }
    }
    
    // 请求类型
    public var method: Moya.Method {
        switch self {
        case .list:
            return .get
        case .intelligentRecommendation:
            return .post
        default:
            return .post
        }
    }
    
    /// 解析格式,这个就是做单元测试模拟的数据,只会在单元测试文件中有作用
    var sampleData: Data {
        return "{}".data(using: String.Encoding.utf8)!
    }
    
    var parameters: [String: Any]? {//请求的get post给服务器的参数
        switch self {
        case let .list(pageNO, pageSize):
            return ["pageNO":pageNO,"pageSize":pageSize]
        case let .intelligentRecommendation(pageNo):
            let mobileTag = USERUDID.replacingOccurrences(of: "-", with: "").lowercased()
            return ["mobileTag" : mobileTag,"pageNo":pageNo]
        default:
            return nil
        
        }
    }
    
    // 请求任务事件(这里附带上参数)
    public var task: Task {
        // 收集参数
        switch self {
        case let .upload(data):
            return .uploadMultipart([MultipartFormData(provider: .data(data), name: "file", fileName: "gif.gif", mimeType: "image/gif")])
        default:
            // 发起请求
            return .requestParameters(parameters: parameters!, encoding: JSONEncoding.default)
        }
    }
    
    /// 是否执行Alamofire验证
    public var validate: Bool {
        return false
    }
    
    /// 公共请求头
    var headers: [String : String]? {
        return ["requestTypeHeader": "app","Content-Type":"application/json"]
    }
}

///endpointClosure
private let myEndpointClosure = { (target: NetworkAPI) -> Endpoint in
///这里的endpointClosure和网上其他实现有些不太一样。
///主要是为了解决URL带有?无法请求正确的链接地址的bug
    let url = target.baseURL.absoluteString + target.path
    var endpoint = Endpoint(
        url: url,
        sampleResponseClosure: { .networkResponse(200, target.sampleData) },
        method: target.method,
        task: target.task,
        httpHeaderFields: target.headers
    )
    switch target {
    case .intelligentRecommendation:
        requestTimeOut = 5 //按照项目需求针对单个API设置不同的超时时长
        return endpoint
    default:
        requestTimeOut = 30 //设置默认的超时时长
        return endpoint
    }
}

private let requestClosure = { (endpoint: Endpoint, done: MoyaProvider.RequestResultClosure) in
    do {
        var request = try endpoint.urlRequest()
        //设置请求时长
        request.timeoutInterval = requestTimeOut
        // 打印请求参数
        if let requestData = request.httpBody {
            print("\(request.url!)"+"\n"+"\(request.httpMethod ?? "")"+"发送参数"+"\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
        }else{
            print("\(request.url!)"+"\(String(describing: request.httpMethod))")
        }
        done(.success(request))
    } catch {
        done(.failure(MoyaError.underlying(error, nil)))
    }
}

// 用Moya默认的Manager还是Alamofire的Manager看实际需求。HTTPS就要手动实现Manager了
//private public func defaultAlamofireManager() -> Manager {
//
//    let configuration = URLSessionConfiguration.default
//
//    configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
//
//    let policies: [String: ServerTrustPolicy] = [
//        "ap.grtstar.cn": .disableEvaluation
//    ]
//    let manager = Alamofire.SessionManager(configuration: configuration,serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies))
//
//    manager.startRequestsImmediately = false
//
//    return manager
//}


/// NetworkActivityPlugin插件用来监听网络请求
private let networkPlugin = NetworkActivityPlugin.init { (changeType, targetType) in

    print("networkPlugin \(changeType)")
    //targetType 是当前请求的基本信息
//    switch(changeType){
//    case .began:
////        print("开始请求网络")
//        
//    case .ended:
////        print("结束")
//    }
}

class NetworkTools {
    ///先添加一个闭包用于成功时后台返回数据的回调
    typealias successCallback = ((String?,String?,String?) -> (Void))
    
    ///再次用一个方法封装provider.request()
    class func EmiyaNetWorkRequest(_ target: NetworkAPI, completion: @escaping successCallback ){
        //先判断网络是否有链接 没有的话直接返回--代码略
        
        //显示hud
        NetworkProvider.request(target) { (result) in
            //隐藏hud
            switch result {
            case let .success(response):
                do {
                    //这里转JSON用的swiftyJSON框架
                    let jsonData = try JSON(data: response.data)
                    //判断后台返回的code码没问题就把数据闭包返回 ,我们后台是0 以实际后台约定为准。
                    if jsonData[RESULT_CODE].stringValue == "0"{
                        completion(String(data: response.data, encoding: String.Encoding.utf8)!,nil,jsonData[RESULT_CODE].stringValue)
                    }else{
                        //flag 不为0 HUD显示错误信息
//                        print("flag不为0显示后台返回message"+"\(jsonData[RESULT_MESSAGE].stringValue)"+"显示code\(jsonData[RESULT_CODE])")
                        
                        completion(nil,jsonData[RESULT_MESSAGE].stringValue,jsonData[RESULT_CODE].stringValue)
                    }
                } catch {
                }
            case let .failure(_):
                completion(nil,"net work request failure","elpsycongroo")
            }
        }
    }
}

使用

import UIKit
import HandyJSON

    fileprivate func setupEssenceListRequestWith(pageNo:Int){
        if (self.pageNo == 1) {
            self.player.stopCurrentPlayingCell()
        }
        
        NetworkTools.EmiyaNetWorkRequest(.intelligentRecommendation(pageNo: pageNo)) { [weak self] (response,msg,code) in
            // 用HandyJSON对返回的数据进行处理 - response:jsonstring
//            print("responseis:\(response)")
            
            if (code != "0"){
                print("flag不为0显示后台返回message"+"\(msg!)"+"显示code\(code!)")
            }else{
                if let essenceLists = JSONDeserializer.deserializeModelArrayFrom(json: response,designatedPath: "data.recommendListDtoList"){
                    if pageNo == 1{
                        self!.essenceListArray.removeAll()
                    }
                    // 请求结束的数据
                    essenceLists.forEach { essenceListModel in
//                        print(essenceListModel!.createrUserId)
                        essenceListModel!.videoTitle = essenceListModel!.recommendTitle
                        essenceListModel!.videoId = essenceListModel!.recommendId
                        essenceListModel!.videoUrl = essenceListModel!.recommendUrl
                        self!.essenceListArray.append(essenceListModel!)
                    }
                    self?.tableView.reloadData()
                    
                    if((self?.essenceListArray.count)! > 0 && self?.pageNo == 1){
                        self?.playTheIndex(index: 0)
                    }
                }
            }
        }

2.熟悉for循环的做法,这里是一套自己想法的算法(包括i--和i++的),只是看递增递减怎么做的

 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let rect = scrollView.contentOffset
        let index = Int(rect.y / (self.view.frame.height - KSTATUSBAR_HEIGHT - KNavigationBarH(controller: self)))
        
        let tempItemsTrueData = self.trueData as NSArray
        let tempItemsHistoryData = self.historyData as NSArray
        
        self.startIndex = tempItemsTrueData.index(of: self.historyData.first!)
        self.endIndex = tempItemsTrueData.index(of: self.historyData.last!)
        
        let madou = self.historyData[index]
        self.currentIndex = tempItemsHistoryData.index(of: madou)
        
        if(index <= 3){
            if(self.startIndex != 0){
                let delta = self.startIndex - 0
                if(delta > 10){ // 加载10条
                    // 递减操作
                    for i in (Int(self.startIndex-10)..= self.historyData.count - 3){
            if(self.endIndex != self.trueData.count - 1){
                for i in self.endIndex+1...self.endIndex+1+10 {
                    let madou = self.trueData[i]
                    madou.indikusu = i
                    self.historyData.append(madou)
                }
            }else{
                for i in self.endIndex+1...self.trueData.count {
                    let madou = self.trueData[i]
                    madou.indikusu = i
                    self.historyData.append(madou)
                }
            }
        }
        
        collectionView.reloadData()
        let genzaiIndikusu = tempItemsHistoryData.index(of: madou)
        let scrollIndex = NSIndexPath(item: genzaiIndikusu, section: 0)
        collectionView.scrollToItem(at: scrollIndex as IndexPath, at: .bottom, animated: false)
        EmiyaLogLine(message: "下标\(index)")
    }

3.OC用的YYLabel,这里用swift翻译,处理展开收起问题

extension AnimationShowViewController{
    fileprivate func setFrame(isExpand:Bool){
        if isExpand {
            self.expandString()
            contentLabel.numberOfLines = 0
        }else{
            self.packUpString()
            contentLabel.numberOfLines = 2
        }
    }
    
    fileprivate func expandString(){
        let attri = contentLabel.attributedText!.mutableCopy() as! NSMutableAttributedString
        attri.append(self.appendAttriStringWith(font: attri.font))
        contentLabel.attributedText = attri
    }
    
    fileprivate func packUpString(){
        let appendText = " 收起 "
        let attri = contentLabel.attributedText!.mutableCopy() as! NSMutableAttributedString
        // 这里是把Range变成NSRange
        let substringRange = attri.string.range(of: appendText,options: .backwards)
        let substringNSRange = attri.string.nsRange(from: substringRange!)
        if (substringNSRange.location != NSNotFound){
            attri.deleteCharacters(in: substringNSRange)
        }
        contentLabel.attributedText = attri
    }
    
    fileprivate func appendAttriStringWith(font:UIFont?) -> NSAttributedString{
        let appentText = " 收起 "
        let append = NSMutableAttributedString.init(string: appentText,attributes: [NSAttributedString.Key.foregroundColor:UIColor.blue,
            NSAttributedString.Key.font:font ?? UIFont.systemFont(ofSize: 16.0)])
        let hi = YYTextHighlight()
        
        // 这里是把Range变成NSRange
        let substringRange = append.string.range(of: appentText)
        let substringNSRange = append.string.nsRange(from: substringRange!)
        append.setTextHighlight(hi, range: substringNSRange)
        // tapAction闭包
        hi.tapAction = { [weak self](containerView, text, range, rect) in
            self?.setFrame(isExpand: false)
        }
        return append
    }
}

// 用到的range,nsrange互相转换的方法:这里参考了网站搜索的方法,谢谢支持
//range转换为NSRange
//扩展的是String类,不可改为NSRange或者Range的扩展,因为samePosition,utf16是String里的
extension String {
    func nsRange(from range: Range) -> NSRange {
        let from = range.lowerBound.samePosition(in: utf16)
        let to = range.upperBound.samePosition(in: utf16)
        return NSRange(location: utf16.distance(from: utf16.startIndex, to: from!),length: utf16.distance(from: from!, to: to!))
    }
}

//NSRange转化为range
//扩展的是String类,不可改为NSRange或者Range的扩展,因为utf16是String里的
extension String {
    func range(from nsRange: NSRange) -> Range? {
        guard
            let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex),
            let to16 = utf16.index(from16, offsetBy: nsRange.length, limitedBy: utf16.endIndex),
            let from = String.Index(from16, within: self),
            let to = String.Index(to16, within: self)
            else { return nil }
        return from ..< to
    }
}

4.关于代理的声明使用(主要是可选实现的代理OC会,swift的我记录下)

// 设置代理
protocol EssenceListViewCellDelegate  { // 代理方法
    /// 点击了用户头像
    func essenceCellDidClickUserIcon(cell:EssenceListViewCell)
    /// 拖动播放进度
    func essenceCellDidClickOnSeek(cell:EssenceListViewCell)
    /// 开始拖动播放进度
    func essenceCellDidClickOnSeekBegin(cell:EssenceListViewCell)
    /// 停止拖动播放进度
    func essenceCellDidClickOnSeekEnd(cell:EssenceListViewCell)
    /// 点击了进度条
    func essenceCellDidTapSliderProgress(cell:EssenceListViewCell)
}

extension EssenceListViewCellDelegate {
    // 在扩展中给出了默认实现的方法,在实际类中就是可选的了
    func essenceCellDidClickUserIcon(cell:EssenceListViewCell){}
    func essenceCellDidClickOnSeek(cell:EssenceListViewCell){}
    func essenceCellDidClickOnSeekBegin(cell:EssenceListViewCell){}
    func essenceCellDidClickOnSeekEnd(cell:EssenceListViewCell){}
    func essenceCellDidTapSliderProgress(cell:EssenceListViewCell){}
}

class EssenceListViewCell: UITableViewCell {
    var delegate : EssenceListViewCellDelegate?    //外部实现代理

    @objc func sliderClick(){
        self.delegate?.essenceCellDidTapSliderProgress(cell: self)
    }
}

// 另外一个控制器去实现其中一个代理方法
extension EssenceListViewController : EssenceListViewCellDelegate{
    func essenceCellDidClickUserIcon(cell: EssenceListViewCell) {
        EmiyaLogLine(message: "头像点击")
    }
}

5.自己声明闭包(目前实现闭包的时候不知道怎么联想出来我的入参,这个还没搞会,都是自己复制粘贴的,也不太会搞逃逸闭包)

// 无参闭包
class EssenceUserView: UIView {
    // 点击头像回调
    var userIconClickCallBack: (() -> Void)?

    @objc func iconVClick(){
        if self.userIconClickCallBack != nil{
            self.userIconClickCallBack!()
        }
    }
}

// 某个角落实现:
userV.userIconClickCallBack = { [weak self] in
      self?.delegate?.essenceCellDidClickUserIcon(cell: self!)
}

// 传参闭包

class JustAView: UIView{
    // 切换全屏回调
    var exchangeFullScreenCallBack: ((_ controlView:JustAView) -> Void)?

    @objc func fullScreenClick(){
        if self.exchangeFullScreenCallBack != nil {
            self.exchangeFullScreenCallBack!(self)
        }
    }
}

// 实现
        self.controlView.exchangeFullScreenCallBack = { [weak self] controlView in
            var orientation : UIInterfaceOrientation = .unknown
            if(self?.player.isFullScreen == true){
                orientation = .portrait
            }else{
                orientation = .landscapeRight
            }
            self?.player.rotate(to: orientation, animated: true)
        }

你可能感兴趣的:(再次写swift的收获)