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)
}