//
|
// Helper.swift
|
// ExhibitionProject
|
//
|
// Created by alvin_y on 2019/1/25.
|
// Copyright © 2019 yang-wang. All rights reserved.
|
//
|
import Foundation
|
import AVKit
|
import UIKit
|
import RxCocoa
|
import RxSwift
|
import MBProgressHUD
|
import SnapKit
|
import IQKeyboardManager
|
import MJRefresh
|
import MapKit
|
import SwiftDate
|
//import TZImagePickerController
|
import Kingfisher
|
import ContactsUI
|
import HandyJSON
|
import CoreTelephony
|
//时间
|
var MSEC_PER_SECOND: Double {
|
return 1000
|
}
|
var MSEC_PER_MIN: Double {
|
return 60 * MSEC_PER_SECOND
|
}
|
var MSEC_PER_HOUR: Double {
|
return 60 * MSEC_PER_MIN
|
}
|
var MSEC_PER_DAY: Double {
|
return 24 * MSEC_PER_HOUR
|
}
|
|
enum YYpermissionsType{
|
/// 相机
|
case camera
|
/// 相册
|
case photo
|
/// 位置
|
case location
|
/// 网络
|
case network
|
/// 麦克风
|
case microphone
|
/// 媒体库
|
case media
|
}
|
|
// 对iphoneX的处理
|
// 屏幕宽高
|
let screenW = UIScreen.main.bounds.width
|
let screenH = UIScreen.main.bounds.height
|
let isIphoneX = (screenH == 812 || screenH > 812) ? true : false
|
let navH:CGFloat = isIphoneX ? 88 : 64
|
let stateH:CGFloat = (isIphoneX ? 88 : 64) - 44
|
let tabBarH:CGFloat = isIphoneX ? 83 : 49
|
let yy_bottomH:CGFloat = isIphoneX ? 34 : 0 // 底部横条
|
let yy_top:CGFloat = isIphoneX ? 34 : 0
|
|
let isExceedIphone5 = screenH > 568.0 ? true : false
|
let isIphone5 = screenH == 568.0
|
let isIphone4 = screenH == 480.0
|
|
|
let Medium = "PingFangSC-Medium"
|
public func Medium(font: Int) -> UIFont{
|
return UIFont.init(name: Medium, size: CGFloat(font))!
|
}
|
let Semibold = "PingFangSC-Semibold"
|
let Bold = "DIN Alternate"
|
////打开相册
|
//func openGallery(from: UIViewController, max: Int, delegate: TZImagePickerControllerDelegate){
|
// from.present(TZImagePickerController(maxImagesCount: max, delegate: delegate), animated: true)
|
//}
|
//打开相册
|
func openContact(from: UIViewController, delegate: CNContactPickerDelegate){
|
let picker = CNContactPickerViewController()
|
//添加可选项目的过滤条件
|
//picker.predicateForEnablingContact = NSPredicate(format: "emailAddresses.@count > 0", argumentArray: nil)
|
picker.delegate = delegate
|
from.present(picker, animated: true, completion: nil)
|
}
|
//打开相册
|
func openSystemPhoto(from:UIViewController,type:UIImagePickerController.SourceType,delegate:UIImagePickerControllerDelegate & UINavigationControllerDelegate) {
|
if UIImagePickerController.isSourceTypeAvailable(type){
|
|
//创建图片控制器
|
let picker = UIImagePickerController()
|
//设置代理
|
picker.delegate = delegate
|
//设置来源
|
picker.sourceType = type
|
//允许编辑
|
picker.allowsEditing = true
|
//打开相机
|
from.present(picker, animated: true, completion: { () -> Void in
|
|
})
|
}else{
|
debugPrint("找不到相机")
|
if type == .camera{
|
alert(text: "找不到相机")
|
}
|
}
|
}
|
|
|
/// 创建条形码
|
///
|
/// - Parameters:
|
/// - messgae: 信息
|
/// - width: 宽度
|
/// - height: 高度
|
/// - Returns: 条形码
|
func generateBarCode(messgae:NSString,width:CGFloat,height:CGFloat) -> UIImage {
|
var returnImage:UIImage?
|
if (messgae.length > 0 && width > 0 && height > 0){
|
let inputData:NSData? = messgae.data(using: String.Encoding.utf8.rawValue)! as NSData
|
// CICode128BarcodeGenerator
|
let filter = CIFilter.init(name: "CICode128BarcodeGenerator")!
|
filter.setValue(inputData, forKey: "inputMessage")
|
var ciImage = filter.outputImage!
|
let scaleX = width/ciImage.extent.size.width
|
let scaleY = height/ciImage.extent.size.height
|
ciImage = ciImage.transformed(by: CGAffineTransform.init(scaleX: scaleX, y: scaleY))
|
returnImage = UIImage.init(ciImage: ciImage)
|
}else {
|
returnImage = nil
|
}
|
return returnImage ?? UIImage()
|
}
|
|
//创建二维码图片
|
/// 创建二维码图片
|
///
|
/// - Parameters:
|
/// - qrString: 内容
|
/// - qrImageName: 图片
|
/// - Returns: 图片
|
func createQRForString(qrString: String?, qrImageName: String?) -> UIImage?{
|
if let sureQRString = qrString {
|
let stringData = sureQRString.data(using: .utf8,allowLossyConversion: false)
|
// 创建一个二维码的滤镜
|
let qrFilter = CIFilter(name: "CIQRCodeGenerator")!
|
qrFilter.setValue(stringData, forKey: "inputMessage")
|
qrFilter.setValue("H", forKey: "inputCorrectionLevel")
|
/// 创建一个CIImage对象
|
let qrCIImage = qrFilter.outputImage
|
// 创建一个颜色滤镜,黑白色
|
/// 功能为变为黑白色
|
let colorFilter = CIFilter(name: "CIFalseColor")!
|
colorFilter.setDefaults()
|
colorFilter.setValue(qrCIImage, forKey: "inputImage")
|
if qrImageName == nil || qrImageName == ""{
|
colorFilter.setValue(CIColor(red: 0, green: 0, blue: 0), forKey: "inputColor0")
|
colorFilter.setValue(CIColor(red: 1, green: 1, blue: 1), forKey: "inputColor1")
|
}else{
|
/// 着色
|
colorFilter.setValue(CIColor(red: 0.8, green: 0.8, blue: 0.8), forKey: "inputColor0")
|
/// 背景色
|
colorFilter.setValue(CIColor(red: 1, green: 1, blue: 1), forKey: "inputColor1")
|
}
|
// #colorLiteral(red: 0.8, green: 0.8, blue: 0.8, alpha: 1)
|
// 返回二维码image
|
let codeImage = UIImage(ciImage: colorFilter.outputImage!.transformed(by: CGAffineTransform(scaleX: 5, y: 5)))
|
/// 二维码中间需要图片
|
// 通常,二维码都是定制的,中间都会放想要表达意思的图片
|
if let iconImage = UIImage(named: qrImageName ?? "") {
|
let rect = CGRect(x:0, y:0, width:codeImage.size.width,height:codeImage.size.height)
|
UIGraphicsBeginImageContext(rect.size)
|
codeImage.draw(in: rect)
|
let avatarSize = CGSize(width:83,height:61)
|
// let x = (rect.width - avatarSize.width) * 0.5
|
// let y = (rect.height - avatarSize.height) * 0.5
|
let x = (rect.width / 2) - (avatarSize.width / 2)
|
let y = (rect.height / 2) - (avatarSize.height / 2)
|
iconImage.draw(in: CGRect(x:x, y:y, width:avatarSize.width,height:avatarSize.height))
|
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
|
UIGraphicsEndImageContext()
|
return resultImage
|
}
|
return codeImage
|
}
|
return nil
|
}
|
///通用的请求分页列表类数据的返回代理
|
protocol RequestPageDataResponseDelegate:NSObjectProtocol {
|
///请求成功需要重新加载数据
|
func onNeedReloadData()
|
///没有任何数据
|
func onNoAnyData()
|
///没有更多数据
|
func onNoMoreData()
|
//获取数据失败
|
func onGetDataFaield(errorinfo info:AnyObject?)
|
|
}
|
|
|
|
|
/// 时间处理
|
///
|
/// - Parameter time: 时间
|
/// - Returns: 格式化字符串
|
func timeFormat(time:Date) -> String {
|
var string = ""
|
let calender = NSCalendar.current
|
let toDay = calender.isDateInToday(time)
|
if toDay{
|
let dateFormatter = DateFormatter()
|
dateFormatter.dateFormat = "MM月dd日 HH:mm"
|
string = "今天 \(dateFormatter.string(from: time))"
|
}else{
|
let dateFormatter = DateFormatter()
|
dateFormatter.dateFormat = "YYYY-MM-dd HH:mm"
|
string = "\(dateFormatter.string(from: time))"
|
}
|
return string
|
}
|
|
|
//换算中文星期
|
func convertToChineseWeekday(date: Date) -> String {
|
switch Calendar.current.component(Calendar.Component.weekday, from: date) {
|
case 1:
|
return "周日"
|
case 2:
|
return "周一"
|
case 3:
|
return "周二"
|
case 4:
|
return "周三"
|
case 5:
|
return "周四"
|
case 6:
|
return "周五"
|
case 7:
|
return "周六"
|
default:
|
return "未知"
|
}
|
}
|
//换算中文星期
|
func convertToChineseWeekdays(date: Date) -> String {
|
switch Calendar.current.component(Calendar.Component.weekday, from: date) {
|
case 1:
|
return "星期日"
|
case 2:
|
return "星期一"
|
case 3:
|
return "星期二"
|
case 4:
|
return "星期三"
|
case 5:
|
return "星期四"
|
case 6:
|
return "星期五"
|
case 7:
|
return "星期六"
|
default:
|
return "未知"
|
}
|
}
|
//通用app
|
var app: AppDelegate {
|
return UIApplication.shared.delegate as! AppDelegate
|
}
|
|
//屏幕宽度
|
var ScreenWidth: CGFloat {
|
return UIScreen.main.bounds.width
|
}
|
|
//屏幕高度
|
var ScreenHeight: CGFloat {
|
return UIScreen.main.bounds.height
|
}
|
|
//Nib
|
|
func NIB(name: String) -> UINib {
|
return UINib(nibName: name, bundle: nil)
|
}
|
|
//快速RBG返回颜色
|
func RGB(r: Int,g: Int,b: Int,a: Int = 255) -> UIColor {
|
return UIColor(red: CGFloat(r)/255.0, green: CGFloat(g)/255.0, blue: CGFloat(b)/255.0, alpha: CGFloat(a)/255)
|
}
|
|
let avatatHolderImage = #imageLiteral(resourceName: "avtar")
|
let placeHolderImage = UIImage.init(named: "driverlicense") ?? UIImage()
|
|
|
|
/// 快速返回storyBoard控制器
|
///
|
/// - Parameters:
|
/// - name: storyBoard名字
|
/// - identifier: storyBoard标识符
|
/// - Returns: UIViewController
|
func storyBoard(name:String,identifier:String) -> UIViewController{
|
return UIStoryboard.init(name: name, bundle: nil).instantiateViewController(withIdentifier: identifier)
|
}
|
|
/// 距离今天N天以后周几
|
///
|
/// - Parameter day: N
|
/// - Returns: 周几
|
func convertWeekday(day:Int) -> String {
|
var date = Date()
|
date = date + day.days
|
return convertToChineseWeekday(date: date)
|
}
|
|
|
/// 距离今天N天以后日期
|
///
|
/// - Parameter day: N
|
/// - Returns: 日期
|
func convertday(day:Int) -> String {
|
var date = Date()
|
date = date + day.days
|
let dateFormatter = DateFormatter()
|
dateFormatter.dateFormat = "MM-dd"
|
return "\(dateFormatter.string(from: date))"
|
}
|
|
/// 距离今天N天以后日期
|
///
|
/// - Parameters:
|
/// - day: N
|
/// - formatter: 格式化
|
/// - Returns: 日期
|
func convertday(day:Int,formatter:String) -> String {
|
var date = Date()
|
date = date + day.days
|
let dateFormatter = DateFormatter()
|
dateFormatter.dateFormat = formatter
|
return "\(dateFormatter.string(from: date))"
|
}
|
|
/// 获取时间Date
|
///
|
/// - Parameters:
|
/// - date: date字符串
|
/// - formatter: 格式化
|
/// - Returns: Date
|
func getDate(date: String,formatter:String) -> Date {
|
let dateformatter = DateFormatter()
|
dateformatter.dateFormat = formatter
|
return dateformatter.date(from: date) ?? Date()
|
}
|
|
/// 获取年
|
///
|
/// - Returns: 年
|
func getYear() -> String {
|
let senddate = Date()
|
let dateformatter = DateFormatter()
|
dateformatter.dateFormat = "yyyy"
|
return dateformatter.string(from: senddate)
|
}
|
|
/// 获取月
|
///
|
/// - Returns: 月
|
func getMonth() -> String {
|
let senddate = Date()
|
let dateformatter = DateFormatter()
|
dateformatter.dateFormat = "MM"
|
return dateformatter.string(from: senddate)
|
}
|
|
/// 获取时间 yyyy-MM-dd HH:mm:ss
|
///
|
/// - Parameter time: Date
|
/// - Returns: yyyy-MM-dd HH:mm:ss
|
func getTimeString(time:Date) -> String{
|
let fmt = DateFormatter()
|
fmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
return fmt.string(from: time)
|
}
|
/// 获取时间 yyyy-MM-dd HH:mm:ss
|
///
|
/// - Parameter time: Date
|
/// - Returns: yyyy-MM-dd HH:mm:ss
|
func getTimeString(time:Date,dateFormat: String) -> String{
|
let fmt = DateFormatter()
|
fmt.dateFormat = dateFormat
|
return fmt.string(from: time)
|
}
|
|
|
/// 计算等待时间 00:00
|
/// - Parameter time: 时间戳
|
func calculateWaitTime(time: Double) -> String {
|
let count = String(format: "%.f", time).count
|
let time = count == 10 ? time : time / 1000
|
let calendar = NSCalendar.current
|
let dateComponents = calendar.dateComponents([Calendar.Component.second,Calendar.Component.minute], from: Date.init(timeIntervalSince1970: time),to: Date().localDate())
|
guard let minute = dateComponents.minute,let second = dateComponents.second else {return ""}
|
return String.init(format: "%02ld:%02ld", minute < 0 ? 0 : minute,second < 0 ? 0 : second)
|
}
|
|
|
|
|
|
//方格布局
|
func layoutGrid(column: Int,
|
rowHeight: CGFloat,
|
leading: CGFloat,
|
trailing: CGFloat,
|
topPadding: CGFloat,
|
bottomPadding: CGFloat,
|
interSpace: CGFloat,
|
lineSpace: CGFloat,
|
subViews: [UIView],
|
container: UIView,
|
containerWidth: CGFloat) {
|
let itemWidth = (containerWidth - leading - trailing - CGFloat(column - 1) * interSpace) / CGFloat(column)
|
let count = subViews.count
|
for i in 0...(count - 1) {
|
let itemView = subViews[i]
|
itemView.snp.makeConstraints{mk in
|
mk.height.equalTo(rowHeight).priority(999)
|
mk.width.equalTo(itemWidth).priority(999)
|
}
|
container.addSubview(itemView)
|
|
if i == 0 {
|
itemView.snp.makeConstraints{mk in
|
mk.top.equalToSuperview().inset(topPadding)
|
mk.leading.equalToSuperview().inset(leading)
|
}
|
} else if i % column == 0 {
|
let p = subViews[i - 1]
|
itemView.snp.makeConstraints{[unowned p]mk in
|
mk.top.equalTo(p.snp.bottom).inset(-lineSpace)
|
mk.leading.equalToSuperview().inset(leading)
|
}
|
} else {
|
let p = subViews[i - 1]
|
itemView.snp.makeConstraints{[unowned p]mk in
|
mk.top.equalTo(p.snp.top)
|
mk.leading.equalTo(p.snp.trailing).inset(-interSpace)
|
}
|
}
|
|
if i == count - 1 {
|
itemView.snp.makeConstraints{mk in
|
mk.bottom.equalToSuperview().inset(bottomPadding)
|
}
|
}
|
|
}
|
}
|
|
|
|
|
/// 限制输入长度
|
///
|
/// - Parameters:
|
/// - length: length
|
/// - textField: textField
|
/// - Returns: Disposable
|
func constrainsTextInputLength(length: Int,textField: UITextField) -> Disposable{
|
return textField.rx.text.subscribe{e in
|
guard let str = e.element else{
|
return
|
}
|
if textField.markedTextRange != nil{
|
return
|
}
|
if (str?.count)! >= length{
|
textField.text = NSString(string: str!).substring(to: length)
|
}
|
}
|
}
|
//限制输入长度
|
func constrainsTextInputLength(length: Int,textView: UITextView) -> Disposable{
|
return textView.rx.text.subscribe{e in
|
guard let str = e.element else{
|
return
|
}
|
if (str?.count)! >= length{
|
textView.text = NSString(string: str!).substring(to: length)
|
}
|
}
|
}
|
|
|
/// 输入类型枚举
|
///
|
/// - Number: 数字
|
/// - Character: 字符
|
/// - CharacterAndNumber: 数字和字符
|
enum InputType {
|
case Number
|
case Character
|
case CharacterAndNumber
|
}
|
|
|
/// 限制输入类型
|
///
|
/// - Parameters:
|
/// - type: 限制类型
|
/// - textField: text
|
/// - Returns: content
|
func constrainsTextInputType(type: InputType,textField: UITextField) -> Disposable{
|
return textField.rx.text.subscribe{e in
|
guard let str = e.element else{
|
return
|
}
|
switch type{
|
case .Number:
|
if isInputNumber(str: str!){
|
textField.text = str!
|
}
|
break
|
case .Character:
|
if isInputRule(str: str!){
|
textField.text = str!
|
}
|
break
|
case .CharacterAndNumber:
|
if isInputRuleAndNumber(str: str!){
|
textField.text = str!
|
}
|
break
|
}
|
}
|
}
|
|
|
|
|
|
|
|
//判断数字
|
func isPurnFloat(string: String) -> Bool {
|
|
let scan: Scanner = Scanner(string: string)
|
|
var val:Float = 0
|
|
return scan.scanFloat(&val) && scan.isAtEnd
|
|
}
|
|
|
/// 验证是否是数字或字符
|
///
|
/// - Parameter str: 验证字符串
|
/// - Returns: 是否数字或字符
|
func isInputRuleAndNumber(str:String) -> Bool {
|
let pred = NSPredicate(format: "SELF MATCHES %@", "[0-9a-zA-Z]*")
|
return pred.evaluate(with: str)
|
}
|
|
/// 判断是否是中文
|
///
|
/// - Parameter str: 验证字符串
|
/// - Returns: 是否是中文
|
func isInputChinese(str:String) -> Bool {
|
let pred = NSPredicate(format: "SELF MATCHES %@", "(^[\\u4e00-\\u9fa5]+$)")
|
return pred.evaluate(with: str)
|
}
|
|
/// 判断是否是中文数字或字符
|
///
|
/// - Parameter str: 验证字符串
|
/// - Returns: 是否是中文
|
func isInputChineseRuleAndNumber(str:String) -> Bool {
|
let pred = NSPredicate(format: "SELF MATCHES %@", "(^[\\u4e00-\\u9fa5]+$)")
|
let pred1 = NSPredicate(format: "SELF MATCHES %@", "[0-9a-zA-Z]*")
|
return pred.evaluate(with: str) || pred1.evaluate(with: str)
|
}
|
|
/// 验证是否是数字
|
///
|
/// - Parameter str: 验证字符串
|
/// - Returns: 是否是数字
|
func isInputNumber(str:String) -> Bool {
|
let pred = NSPredicate(format: "SELF MATCHES %@", "[0-9]*")
|
return pred.evaluate(with: str)
|
}
|
/// 验证是否是数字(带点)
|
///
|
/// - Parameter str: 验证字符串
|
/// - Returns: 是否是数字
|
func isInputPointandNumber(str:String) -> Bool {
|
let pred = NSPredicate(format: "SELF MATCHES %@", "[0-9.]*")
|
return pred.evaluate(with: str)
|
}
|
/// 验证是否是字符
|
///
|
/// - Parameter str: 验证字符串
|
/// - Returns: 是否是字符
|
func isInputRule(str:String) -> Bool {
|
let pred = NSPredicate(format: "SELF MATCHES %@", "[a-zA-Z]*")
|
return pred.evaluate(with: str)
|
}
|
|
|
|
|
|
|
|
|
//打开系统导航
|
func openNativeNavigation(lng: Double, lat: Double){
|
let loc = CLLocationCoordinate2DMake(lat, lng)
|
let currentLocation = MKMapItem.forCurrentLocation()
|
let toLocation = MKMapItem(placemark:MKPlacemark(coordinate:loc,addressDictionary:nil))
|
MKMapItem.openMaps(with: [currentLocation,toLocation], launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving,MKLaunchOptionsShowsTrafficKey: NSNumber(value: true)])
|
}
|
|
//打电话
|
func call(number: String){
|
guard let url = URL(string: "tel://\(number)") else {
|
|
return
|
}
|
if #available(iOS 10.0, *) {
|
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
} else {
|
UIApplication.shared.openURL(url)
|
}
|
|
}
|
|
/// 打开网址
|
///
|
/// - Parameter url: 网址
|
func openURL(url: String){
|
|
var openUrl = url
|
if !openUrl.contains("https") || !openUrl.contains("http"){
|
openUrl = "http://\(openUrl)"
|
}
|
guard let url = URL(string: openUrl) else {
|
return
|
}
|
if #available(iOS 10.0, *) {
|
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
} else {
|
UIApplication.shared.openURL(url)
|
}
|
|
}
|
|
|
//关闭键盘
|
func closeKeyboard(){
|
if IQKeyboardManager.shared().isKeyboardShowing {
|
IQKeyboardManager.shared().resignFirstResponder()
|
}
|
}
|
|
//获取麦克风权限
|
func requestMicroPhonePermission(){
|
AVCaptureDevice.requestAccess(for: .audio, completionHandler: {_ in })
|
}
|
|
//检查麦克风权限
|
func checkMicroPhonePermission() -> Bool{
|
return AVCaptureDevice.authorizationStatus(for: .audio) == AVAuthorizationStatus.authorized
|
}
|
|
|
|
//func typeCheck(needs:[(UITextField,TypeCheck)]) -> [String]?{
|
// var results: [String] = []
|
// for check in needs {
|
// let tf = check.0
|
// let type = check.1
|
// if let text = tf.text?.byReplacing(),text.count > 0{
|
// let taple = type.check(text: text)
|
// if taple.0 {
|
// results.append(taple.1)
|
// }else{
|
// alert(msg: taple.1)
|
// return nil
|
// }
|
// }else{
|
// alert(msg: "\(type.name()) Can't be empty")
|
// return nil
|
// }
|
// }
|
// return results
|
//}
|
|
//电话横显示
|
func phoneGapDisplay(phone: String) -> String {
|
var temp = phone
|
let startIndex = temp.startIndex
|
if phone.count >= 3 {
|
temp.insert("-", at: temp.index(startIndex, offsetBy: 3))
|
}
|
if phone.count >= 7 {
|
temp.insert("-", at: temp.index(startIndex, offsetBy: 8))
|
}
|
return temp
|
}
|
|
//不为空即可用
|
func notEmptyEnable(fields: [UITextField]) -> Observable<Bool> {
|
return Observable<Bool>.combineLatest(fields.compactMap { return $0.rx.text.orEmpty }){ obs in
|
return obs.reduce(true, { r, n in
|
return r && !n.isEmpty
|
})
|
}
|
}
|
|
|
|
//提示框
|
func alert(text: String) {
|
DispatchQueue.main.async {
|
let hud = MBProgressHUD.showAdded(to: app.window!, animated: true)
|
hud.mode = .text
|
hud.label.text = text
|
hud.label.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
|
hud.bezelView.style = .solidColor
|
hud.bezelView.color = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.7)
|
hud.label.numberOfLines = 0
|
hud.label.font = UIFont.boldSystemFont(ofSize: 14)
|
hud.hide(animated: true, afterDelay: 1.7)
|
}
|
}
|
func alret(afterDelay:Double=1.7){
|
let hud = MBProgressHUD.showAdded(to: app.window!, animated: true)
|
hud.mode = .determinate
|
hud.bezelView.style = .solidColor
|
hud.bezelView.color = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.7)
|
hud.hide(animated: true, afterDelay: 1.7)
|
}
|
func alretShow(){
|
let hud = MBProgressHUD.showAdded(to: app.window!, animated: true)
|
hud.mode = .determinate
|
hud.bezelView.style = .solidColor
|
hud.bezelView.color = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.7)
|
|
}
|
func alretDismiss(){
|
MBProgressHUD.hide(for: app.window!, animated: true)
|
}
|
//提示框
|
func alertSuccessful(text: String,completionBlock:@escaping () ->Void) {
|
let hud = MBProgressHUD.showAdded(to: app.window!, animated: true)
|
hud.mode = .customView
|
hud.customView = UIImageView.init(image: UIImage.init(named: "succeed"))
|
hud.label.text = text
|
hud.label.textColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.8)
|
hud.bezelView.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
|
hud.backgroundView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.7)
|
hud.label.numberOfLines = 0
|
hud.label.font = UIFont.boldSystemFont(ofSize: 14)
|
hud.hide(animated: true, afterDelay: 1.7)
|
hud.completionBlock = {
|
completionBlock()
|
}
|
}
|
|
//压缩图片
|
func resizeImage(originalImg:UIImage,maxWidth: CGFloat = 1280,maxHeight: CGFloat = 720) -> UIImage{
|
|
//prepare constants
|
let width = originalImg.size.width
|
let height = originalImg.size.height
|
let scale = width/height
|
|
var sizeChange = CGSize()
|
|
if width <= maxWidth && height <= maxWidth{ //a,图片宽或者高均小于或等于1280时图片尺寸保持不变,不改变图片大小
|
return originalImg
|
}else if width > maxWidth || height > maxWidth {//b,宽或者高大于1280,但是图片宽度高度比小于或等于2,则将图片宽或者高取大的等比压缩至1280
|
|
if scale <= 2 && scale >= 1 {
|
let changedWidth:CGFloat = maxWidth
|
let changedheight:CGFloat = changedWidth / scale
|
sizeChange = CGSize(width: changedWidth, height: changedheight)
|
|
}else if scale >= 0.5 && scale <= 1 {
|
|
let changedheight:CGFloat = maxWidth
|
let changedWidth:CGFloat = changedheight * scale
|
sizeChange = CGSize(width: changedWidth, height: changedheight)
|
|
}else if width > maxWidth && height > maxWidth {//宽以及高均大于1280,但是图片宽高比大于2时,则宽或者高取小的等比压缩至1280
|
|
if scale > 2 {//高的值比较小
|
|
let changedheight:CGFloat = maxWidth
|
let changedWidth:CGFloat = changedheight * scale
|
sizeChange = CGSize(width: changedWidth, height: changedheight)
|
|
}else if scale < 0.5{//宽的值比较小
|
|
let changedWidth:CGFloat = maxWidth
|
let changedheight:CGFloat = changedWidth / scale
|
sizeChange = CGSize(width: changedWidth, height: changedheight)
|
|
}
|
}else {//d, 宽或者高,只有一个大于1280,并且宽高比超过2,不改变图片大小
|
return originalImg
|
}
|
}
|
|
UIGraphicsBeginImageContext(sizeChange)
|
|
//draw resized image on Context
|
originalImg.draw(in: CGRect(x:0, y:0, width:sizeChange.width, height:sizeChange.height))
|
|
//create UIImage
|
let resizedImg = UIGraphicsGetImageFromCurrentImageContext()
|
|
UIGraphicsEndImageContext()
|
|
return resizedImg!
|
|
}
|
//计数器代理
|
protocol TimeTickDelegate: class {
|
func onTickCount(count: Int)
|
}
|
//计数器 每秒执行一次
|
class TimeTick: NSObject{
|
var count: Int = 0
|
var timer: Timer?
|
weak var delegate: TimeTickDelegate?
|
|
func start(delegate: TimeTickDelegate){
|
if timer != nil {
|
timer?.invalidate()
|
timer = nil
|
}
|
self.delegate = delegate
|
count = 0
|
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(onCall), userInfo: nil, repeats: true)
|
}
|
|
func stop(){
|
count = 0
|
timer?.invalidate()
|
timer = nil
|
}
|
|
@objc func onCall(){
|
count += 1
|
delegate?.onTickCount(count: count)
|
}
|
|
}
|
|
//上下拉列表协议
|
protocol Refreshable: class{
|
func requestData()
|
var tableView: UITableView { get }
|
}
|
|
//上下拉列表控制
|
class RefreshableListHandler<T>: NSObject {
|
weak var delegate: Refreshable?
|
var items: [T] = [T]()
|
var currentPage: Int = 1
|
var nextPage: Int = 1
|
|
func config(delegate: Refreshable){
|
self.delegate = delegate
|
self.delegate?.tableView.mj_header = MJRefreshNormalHeader(refreshingTarget: self, refreshingAction: #selector(onRefresh))
|
self.delegate?.tableView.mj_footer = MJRefreshBackNormalFooter(refreshingTarget: self, refreshingAction: #selector(onNextPage))
|
// self.delegate?.tableView.mj_footer.isAutomaticallyHidden = true
|
}
|
|
@objc func onRefresh(){
|
nextPage = 1
|
delegate?.requestData()
|
}
|
|
@objc func onNextPage(){
|
nextPage = currentPage + 1
|
delegate?.requestData()
|
}
|
|
func errorOccur(){
|
delegate?.tableView.mj_header?.endRefreshing()
|
delegate?.tableView.mj_footer?.endRefreshing()
|
}
|
|
func endRefreshing(){
|
currentPage = nextPage
|
delegate?.tableView.mj_header?.endRefreshing()
|
delegate?.tableView.mj_footer?.endRefreshing()
|
}
|
|
func endRefreshingWithnomoreData(){
|
delegate?.tableView.mj_footer?.endRefreshingWithNoMoreData()
|
}
|
|
func endRefreshing(newData: [T]){
|
if nextPage == 1 {
|
items.removeAll()
|
items.append(contentsOf: newData)
|
delegate?.tableView.mj_header?.endRefreshing()
|
delegate?.tableView.reloadData()
|
currentPage = nextPage
|
}else{
|
if newData.count == 0 {
|
delegate?.tableView.mj_footer?.endRefreshingWithNoMoreData()
|
}else{
|
items.append(contentsOf: newData)
|
delegate?.tableView.mj_footer?.endRefreshing()
|
delegate?.tableView.reloadData()
|
currentPage = nextPage
|
}
|
}
|
}
|
|
}
|
|
class ScaleInMiddleCollectionViewLayout: UICollectionViewFlowLayout {
|
|
var cellSize = CGSize(width: (ScreenWidth + 20) / 3, height: 90)
|
|
required init(coder aDecoder: NSCoder) {
|
super.init(coder: aDecoder)!
|
scrollDirection = UICollectionView.ScrollDirection.horizontal //1
|
itemSize = cellSize //2
|
minimumInteritemSpacing = 10 //3
|
}
|
|
override func prepare() {
|
super.prepare()
|
//The rate at which we scroll the collection view.
|
//1
|
collectionView?.decelerationRate = UIScrollView.DecelerationRate.fast
|
//2
|
collectionView?.contentInset = UIEdgeInsets(
|
top: 0,
|
left: collectionView!.bounds.width / 2 - cellSize.width / 2,
|
bottom: 0,
|
right: collectionView!.bounds.width / 2 - cellSize.width / 2
|
)
|
}
|
|
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
|
//1
|
guard let array = super.layoutAttributesForElements(in: rect) else { return nil }
|
|
//2
|
for attributes in array {
|
//3
|
let frame = attributes.frame
|
//4
|
let distance = abs(collectionView!.contentOffset.x + collectionView!.contentInset.left - frame.origin.x)
|
//5
|
let scale = min(max(1.2 - distance / (collectionView!.bounds.width), 1), 1.2)
|
//6
|
attributes.transform = CGAffineTransform(scaleX: scale, y: scale)
|
}
|
|
return array
|
}
|
|
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
|
return true
|
}
|
|
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
|
// Snap cells to centre
|
//1
|
var newOffset = CGPoint()
|
//2
|
let layout = collectionView!.collectionViewLayout as! UICollectionViewFlowLayout
|
//3
|
let width = layout.itemSize.width + layout.minimumLineSpacing
|
//4
|
var offset = proposedContentOffset.x + collectionView!.contentInset.left
|
|
//5
|
if velocity.x > 0 {
|
//ceil returns next biggest number
|
offset = width * ceil(offset / width)
|
} else if velocity.x == 0 { //6
|
//rounds the argument
|
offset = width * round(offset / width)
|
} else if velocity.x < 0 { //7
|
//removes decimal part of argument
|
offset = width * floor(offset / width)
|
}
|
//8
|
newOffset.x = offset - collectionView!.contentInset.left
|
newOffset.y = proposedContentOffset.y //y will always be the same...
|
return newOffset
|
}
|
|
}
|
|
class CodeTimeTick: TimeTickDelegate {
|
|
weak var codeBt: UIButton?
|
|
var timeTick: TimeTick = TimeTick()
|
|
var grayColor: UIColor = UIColor.color(light: UIColor.color(hexString: "#333333"), dark: UIColor.color(hexString: "#333333"))
|
|
var originalColor: UIColor = ThemeColor
|
|
func config(button: UIButton,grayColor: UIColor){
|
codeBt = button
|
self.grayColor = grayColor
|
originalColor = button.titleLabel?.textColor ?? ThemeColor
|
}
|
|
func onTickCount(count: Int) {
|
if count == 60 {
|
timeTick.stop()
|
codeBt?.setTitle("获取验证码", for: .normal)
|
codeBt?.setTitle("获取验证码", for: .disabled)
|
codeBt?.isEnabled = true
|
codeBt?.setTitleColor(originalColor, for: .normal)
|
}else{
|
codeBt?.setTitle("已发送 \(60 - count)s", for: .disabled)
|
codeBt?.layer.borderColor = UIColor.clear.cgColor
|
codeBt?.setTitleColor(grayColor, for: .disabled)
|
}
|
}
|
|
func start(){
|
codeBt?.isEnabled = false
|
timeTick.start(delegate: self)
|
}
|
|
func close(){
|
timeTick.stop()
|
codeBt?.setTitle("获取验证码", for: .normal)
|
codeBt?.isEnabled = true
|
}
|
|
}
|
|
|
/** 屏幕尺寸 */
|
|
// 屏幕宽度
|
let SCREEN_HEIGHT = UIScreen.main.bounds.size.height
|
// 屏幕高度
|
let SCREEN_WIDTH = UIScreen.main.bounds.size.width
|
// 自适应屏幕宽度
|
func FIT_SCREEN_WIDTH(_ size: CGFloat) -> CGFloat {
|
return size * SCREEN_WIDTH / 375.0
|
}
|
// 自适应屏幕高度
|
func FIT_SCREEN_HEIGHT(_ size: CGFloat) -> CGFloat {
|
return size * SCREEN_HEIGHT / 667.0
|
}
|
// 自适应屏幕字体大小
|
func AUTO_FONT(_ size: CGFloat) -> UIFont {
|
let autoSize = size * SCREEN_WIDTH / 375.0
|
return UIFont.systemFont(ofSize: autoSize)
|
}
|
|
|
|
|
|
/// 微信支付
|
///
|
/// - Parameter dict: 字典信息
|
//func weChatPay(dict: [String:Any],_ complete:@escaping () -> Void,fail:@escaping () -> Void) {
|
// AISharedPay.handleWeixinPayment(dict) { (success, obj, msg) in
|
// if success{
|
// alert(msg: "支付成功")
|
// complete()
|
// }else{
|
// alert(msg: msg ?? "支付失败")
|
// fail()
|
// }
|
// }
|
//}
|
///// 支付宝支付
|
/////
|
///// - Parameter info: 信息
|
//func aliPay(info: String,_ complete:@escaping () -> Void,fail:@escaping () -> Void) {
|
// AISharedPay.handleAlipay(info) { (success, obj, msg) in
|
// if success{
|
// DispatchQueue.main.async {
|
// alert(msg: "支付成功")
|
// complete()
|
// }
|
// }else{
|
// DispatchQueue.main.async {
|
// alert(msg: msg ?? "支付失败")
|
// fail()
|
// }
|
// }
|
// }
|
//}
|
|
// MARK: - 检测是否开启定位
|
/// 检测是否开启定位
|
func openLocationServiceWithBlock(_ isSet:Bool? = nil,_ action :@escaping ((Bool)->())) {
|
var isOpen = false
|
// if CLLocationManager.locationServicesEnabled() || CLLocationManager.authorizationStatus() != .denied {
|
// isOpen = true
|
// }
|
if CLLocationManager.authorizationStatus() == .authorizedAlways || CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
|
isOpen = true
|
}
|
if isOpen == false && isSet == true {yy_openURL(.location)}
|
action(isOpen)
|
}
|
|
// MARK: - 跳转系统设置界面
|
func yy_openURL(_ type: YYpermissionsType? = nil) {
|
let title = "访问受限"
|
var message = "请点击“前往”,允许访问权限"
|
let appName: String = "宽窄优行" //App 名称
|
if type == .camera { // 相机
|
message = "请在iPhone的\"设置-隐私-相机\"选项中,允许\"\(appName)\"访问你的相机"
|
} else if type == .photo { // 相册
|
message = "请在iPhone的\"设置-隐私-照片\"选项中,允许\"\(appName)\"访问您的相册"
|
} else if type == .location { // 位置
|
message = "请在iPhone的\"设置-隐私-定位服务\"选项中,允许\"\(appName)\"访问您的位置,获得更多商品信息"
|
} else if type == .network { // 网络
|
message = "请在iPhone的\"设置-蜂窝移动网络\"选项中,允许\"\(appName)\"访问您的移动网络"
|
} else if type == .microphone { // 麦克风
|
message = "请在iPhone的\"设置-隐私-麦克风\"选项中,允许\"\(appName)\"访问您的麦克风"
|
} else if type == .media { // 媒体库
|
message = "请在iPhone的\"设置-隐私-媒体与Apple Music\"选项中,允许\"\(appName)\"访问您的媒体库"
|
}
|
let url = URL(string: UIApplication.openSettingsURLString)
|
let alertController = UIAlertController(title: title,
|
message: message,
|
preferredStyle: .alert)
|
let cancelAction = UIAlertAction(title:"取消", style: .cancel, handler:nil)
|
let settingsAction = UIAlertAction(title:"前往", style: .default, handler: {
|
(action) -> Void in
|
if UIApplication.shared.canOpenURL(url!) {
|
if #available(iOS 10, *) {
|
UIApplication.shared.open(url!, options: [:],completionHandler: {(success) in})
|
} else {
|
UIApplication.shared.openURL(url!)
|
}
|
}
|
})
|
alertController.addAction(cancelAction)
|
alertController.addAction(settingsAction)
|
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
|
}
|
// MARK: - 验证
|
enum Validate {
|
case email(_: String)
|
case phoneNum(_: String)
|
case carNum(_: String)
|
case username(_: String)
|
case password(_: String)
|
case nickname(_: String)
|
|
case URL(_: String)
|
case IP(_: String)
|
|
|
var isRight: Bool {
|
var predicateStr:String!
|
var currObject:String!
|
switch self {
|
case let .email(str):
|
predicateStr = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$"
|
currObject = str
|
case let .phoneNum(str):
|
predicateStr = "^((13[0-9])|(15[^4,\\D]) |(17[0,0-9])|(18[0,0-9]))\\d{8}$"
|
currObject = str
|
case let .carNum(str):
|
predicateStr = "^[A-Za-z]{1}[A-Za-z_0-9]{5}$"
|
currObject = str
|
case let .username(str):
|
predicateStr = "^[A-Za-z0-9]{6,20}+$"
|
currObject = str
|
case let .password(str):
|
predicateStr = "^[a-zA-Z0-9]{6,20}+$"
|
currObject = str
|
case let .nickname(str):
|
predicateStr = "^[\\u4e00-\\u9fa5]{4,8}$"
|
currObject = str
|
case let .URL(str):
|
predicateStr = "^(http?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$"
|
currObject = str
|
case let .IP(str):
|
predicateStr = "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
|
currObject = str
|
}
|
|
let predicate = NSPredicate(format: "SELF MATCHES %@" ,predicateStr)
|
return predicate.evaluate(with: currObject)
|
}
|
}
|
|
|
/// 设置空页面
|
/// - Parameters:
|
/// - view: <#view description#>
|
/// - emptyTitle: <#emptyTitle description#>
|
func scrollViewEmptyAssistant(view:UIScrollView,emptyTitle:String="暂无数据"){
|
FORScrollViewEmptyAssistant.empty(withContentView: view, configerBlock: { (configer) in
|
configer?.emptyTitle = emptyTitle
|
configer?.emptyTitleFont = UIFont.systemFont(ofSize: 15)
|
})
|
}
|