312 lines
9.9 KiB
Swift
312 lines
9.9 KiB
Swift
|
|
//
|
|||
|
|
// UIView+Ext.swift
|
|||
|
|
// DouYinSwift5
|
|||
|
|
//
|
|||
|
|
// Created by lym on 2020/7/23.
|
|||
|
|
// Copyright © 2020 lym. All rights reserved.
|
|||
|
|
//
|
|||
|
|
|
|||
|
|
import UIKit
|
|||
|
|
|
|||
|
|
|
|||
|
|
public func adaptWidth(designWidth: CGFloat = 375.0, _ vale: CGFloat) -> CGFloat {
|
|||
|
|
return UIScreen.main.bounds.size.width / designWidth * vale
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - 属性
|
|||
|
|
|
|||
|
|
@objc extension UIView {
|
|||
|
|
/// 边框颜色
|
|||
|
|
@IBInspectable var borderColor: UIColor? {
|
|||
|
|
get {
|
|||
|
|
guard let color = layer.borderColor else { return nil }
|
|||
|
|
return UIColor(cgColor: color)
|
|||
|
|
}
|
|||
|
|
set {
|
|||
|
|
guard let color = newValue else {
|
|||
|
|
layer.borderColor = nil
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
layer.borderColor = color.cgColor
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 边框宽度
|
|||
|
|
@IBInspectable var borderWidth: CGFloat {
|
|||
|
|
get { return layer.borderWidth }
|
|||
|
|
set { layer.borderWidth = newValue }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 圆角半径
|
|||
|
|
@IBInspectable var cornerRadius: CGFloat {
|
|||
|
|
get { return layer.cornerRadius }
|
|||
|
|
set {
|
|||
|
|
layer.cornerRadius = newValue
|
|||
|
|
layer.masksToBounds = true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var size: CGSize {
|
|||
|
|
get { return frame.size }
|
|||
|
|
set { width = newValue.width; height = newValue.height }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var width: CGFloat {
|
|||
|
|
get { return frame.size.width }
|
|||
|
|
set { return frame.size.width = newValue }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var height: CGFloat {
|
|||
|
|
get { return frame.size.height }
|
|||
|
|
set { return frame.size.height = newValue }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var x: CGFloat {
|
|||
|
|
get { return frame.origin.x }
|
|||
|
|
set { frame.origin.x = newValue }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var y: CGFloat {
|
|||
|
|
get { return frame.origin.y }
|
|||
|
|
set { frame.origin.y = newValue }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var centerX: CGFloat {
|
|||
|
|
get { return center.x }
|
|||
|
|
set { center.x = newValue }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var centerY: CGFloat {
|
|||
|
|
get { return center.y }
|
|||
|
|
set { center.y = newValue }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var origin: CGPoint {
|
|||
|
|
get { return frame.origin }
|
|||
|
|
set { frame.origin = newValue }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@objc public extension UIView {
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// 删除所有的子视图
|
|||
|
|
func removeSubviews() {
|
|||
|
|
subviews.forEach { $0.removeFromSuperview() }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 添加阴影
|
|||
|
|
/// - Parameters:
|
|||
|
|
/// - color: 颜色
|
|||
|
|
/// - radius: 半径
|
|||
|
|
/// - offset: 偏移
|
|||
|
|
/// - opacity: 透明度
|
|||
|
|
func addShadow(ofColor color: UIColor = UIColor(red: 0.07, green: 0.47, blue: 0.57, alpha: 1.0),
|
|||
|
|
radius: CGFloat = 3,
|
|||
|
|
offset: CGSize = .zero,
|
|||
|
|
opacity: Float = 0.5)
|
|||
|
|
{
|
|||
|
|
layer.shadowColor = color.cgColor
|
|||
|
|
layer.shadowOffset = offset
|
|||
|
|
layer.shadowRadius = radius
|
|||
|
|
layer.shadowOpacity = opacity
|
|||
|
|
layer.masksToBounds = false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 返回视图的控制器对象
|
|||
|
|
///
|
|||
|
|
/// - Returns: 控制器对象,可能为空
|
|||
|
|
func viewController() -> UIViewController? {
|
|||
|
|
var view: UIView? = self
|
|||
|
|
repeat {
|
|||
|
|
if let nextResponder = view?.next {
|
|||
|
|
if nextResponder.isKind(of: UIViewController.self) {
|
|||
|
|
return nextResponder as? UIViewController
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
view = view?.superview
|
|||
|
|
} while view != nil
|
|||
|
|
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@objc extension UIView {
|
|||
|
|
@discardableResult
|
|||
|
|
/// 添加渐变背景
|
|||
|
|
func addGradientColor(startPoint: CGPoint,
|
|||
|
|
endPoint: CGPoint,
|
|||
|
|
locs: [NSNumber] = [0, 1],
|
|||
|
|
colors: [Any],
|
|||
|
|
cornerRadius: CGFloat = 0) -> CAGradientLayer?
|
|||
|
|
{
|
|||
|
|
guard startPoint.x >= 0,
|
|||
|
|
startPoint.x <= 1,
|
|||
|
|
startPoint.y >= 0,
|
|||
|
|
startPoint.y <= 1,
|
|||
|
|
endPoint.x >= 0,
|
|||
|
|
endPoint.x <= 1,
|
|||
|
|
endPoint.y >= 0,
|
|||
|
|
endPoint.y <= 1
|
|||
|
|
else {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
layoutIfNeeded()
|
|||
|
|
|
|||
|
|
var gradientLayer: CAGradientLayer!
|
|||
|
|
|
|||
|
|
removeGradientLayer()
|
|||
|
|
|
|||
|
|
gradientLayer = CAGradientLayer()
|
|||
|
|
gradientLayer.frame = layer.bounds
|
|||
|
|
gradientLayer.startPoint = startPoint
|
|||
|
|
gradientLayer.endPoint = endPoint
|
|||
|
|
gradientLayer.colors = colors
|
|||
|
|
gradientLayer.cornerRadius = layer.cornerRadius
|
|||
|
|
gradientLayer.masksToBounds = true
|
|||
|
|
gradientLayer.locations = locs
|
|||
|
|
|
|||
|
|
if cornerRadius > 0 {
|
|||
|
|
let shapeLayer = CAShapeLayer()
|
|||
|
|
shapeLayer.borderWidth = 1
|
|||
|
|
shapeLayer.path = UIBezierPath(roundedRect: gradientLayer.bounds, cornerRadius: cornerRadius).cgPath
|
|||
|
|
shapeLayer.fillColor = UIColor.clear.cgColor // 必须要设置成clearColor或者nil,默认是黑色
|
|||
|
|
shapeLayer.strokeColor = UIColor.white.cgColor // 随便设置一个非clearColor的颜色
|
|||
|
|
gradientLayer.mask = shapeLayer
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 渐变图层插入到最底层,避免在uibutton上遮盖文字图片
|
|||
|
|
layer.insertSublayer(gradientLayer, at: 0)
|
|||
|
|
backgroundColor = UIColor.clear
|
|||
|
|
// self如果是UILabel,masksToBounds设为true会导致文字消失
|
|||
|
|
// layer.masksToBounds = false
|
|||
|
|
return gradientLayer
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public func removeGradientLayer() {
|
|||
|
|
if let sl = layer.sublayers {
|
|||
|
|
for layer in sl {
|
|||
|
|
if layer.isKind(of: CAGradientLayer.self) {
|
|||
|
|
layer.removeFromSuperlayer()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func addDashedBorder(to view: UIView, lineLength: CGFloat = 8, lineSpacing: CGFloat = 2, cornerRadius: CGFloat = 0) {
|
|||
|
|
let shapeLayer = CAShapeLayer()
|
|||
|
|
shapeLayer.name = "DashedBorder"
|
|||
|
|
let lineColor = UIColor.c.con
|
|||
|
|
// let lineColor = UIColor.red
|
|||
|
|
|
|||
|
|
shapeLayer.bounds = view.bounds
|
|||
|
|
shapeLayer.position = CGPoint(x: view.bounds.width / 2, y: view.bounds.height / 2)
|
|||
|
|
shapeLayer.fillColor = UIColor.clear.cgColor
|
|||
|
|
shapeLayer.strokeColor = lineColor.cgColor
|
|||
|
|
shapeLayer.lineWidth = 1
|
|||
|
|
shapeLayer.lineJoin = .round
|
|||
|
|
shapeLayer.lineDashPattern = [NSNumber(value: Float(lineLength)), NSNumber(value: Float(lineSpacing))]
|
|||
|
|
|
|||
|
|
let path = UIBezierPath(roundedRect: view.bounds, cornerRadius: cornerRadius)
|
|||
|
|
shapeLayer.path = path.cgPath
|
|||
|
|
|
|||
|
|
// 先移除旧的虚线层(避免重复添加)
|
|||
|
|
view.layer.sublayers?.removeAll(where: { $0.name == "DashedBorder" })
|
|||
|
|
|
|||
|
|
view.layer.addSublayer(shapeLayer)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
extension UIView{
|
|||
|
|
/// 将一个矩形区域从指定视图坐标系转换到当前视图或窗口坐标系
|
|||
|
|
///
|
|||
|
|
/// 使用方法:
|
|||
|
|
/// ```
|
|||
|
|
/// CGRect rect = [self.view convertRect:_button.frame fromViewOrWindow:_button.superview];
|
|||
|
|
/// ```
|
|||
|
|
///
|
|||
|
|
/// button的frame是相对于其superview来确定的,frame确定了button在其superview的位置和大小
|
|||
|
|
///
|
|||
|
|
/// 一般来说,toView方法中,消息的接收者为被转换的frame所在的控件的superview;fromView方法中,消息的接收者为即将转到的目标view.
|
|||
|
|
///
|
|||
|
|
/// - Parameters:
|
|||
|
|
/// - rect: 矩形区域
|
|||
|
|
/// - view: 指定视图或窗口
|
|||
|
|
/// - Returns: 当前坐标系的矩形区域
|
|||
|
|
func convertRect(rect: CGRect, fromViewOrWindow view: UIView?) -> CGRect {
|
|||
|
|
guard let view = view else {
|
|||
|
|
if isKind(of: UIWindow.self) {
|
|||
|
|
return (self as! UIWindow).convert(rect, from: nil)
|
|||
|
|
} else {
|
|||
|
|
return convert(rect, from: nil)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if let from = view.isKind(of: UIWindow.self) ? (view as! UIWindow) : view.window,
|
|||
|
|
let to = isKind(of: UIWindow.self) ? (self as! UIWindow) : window, from != to {
|
|||
|
|
var r = rect
|
|||
|
|
r = from.convert(r, from: view)
|
|||
|
|
r = to.convert(r, from: from)
|
|||
|
|
r = convert(r, from: to)
|
|||
|
|
return r
|
|||
|
|
} else {
|
|||
|
|
return convert(rect, from: view)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
extension UIView{
|
|||
|
|
/// 扩展UIView增加抖动方法
|
|||
|
|
///
|
|||
|
|
/// - Parameters:
|
|||
|
|
/// - isVertical: 抖动方向(默认是水平方向)
|
|||
|
|
/// - times: 抖动次数(默认5次)
|
|||
|
|
/// - interval: 每次抖动时间(默认0.1秒)
|
|||
|
|
/// - delta: 抖动偏移量(默认2)
|
|||
|
|
/// - completion: 抖动动画结束后的回调
|
|||
|
|
public func shake(_ isVertical: Bool = false,
|
|||
|
|
_ times: Int = 5,
|
|||
|
|
_ interval: TimeInterval = 0.1,
|
|||
|
|
_ delta: CGFloat = 2,
|
|||
|
|
_ completion: (() -> ())? = nil) {
|
|||
|
|
// 播放动画
|
|||
|
|
UIView.animate(withDuration: interval) {
|
|||
|
|
var transform = CGAffineTransform(translationX: 0, y: delta)
|
|||
|
|
if isVertical == true {
|
|||
|
|
transform = CGAffineTransform(translationX: delta, y: 0)
|
|||
|
|
}
|
|||
|
|
self.layer.setAffineTransform(transform)
|
|||
|
|
} completion: {_ in
|
|||
|
|
// 如果当前是最后一次抖动,则将位置还原,并调用完成回调函数
|
|||
|
|
if (times == 0) {
|
|||
|
|
UIView.animate(withDuration: interval) {
|
|||
|
|
self.layer.setAffineTransform(CGAffineTransform.identity)
|
|||
|
|
} completion: { _ in
|
|||
|
|
completion?()
|
|||
|
|
}
|
|||
|
|
}else {
|
|||
|
|
// 如果当前不是最后一次抖动,则继续播放动画(总次数减1,偏移位置变成相反的)
|
|||
|
|
self.shake(isVertical, times - 1, interval, -delta, completion)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
extension UIView {
|
|||
|
|
//将当前视图转为UIImage
|
|||
|
|
func asImage() -> UIImage {
|
|||
|
|
let renderer = UIGraphicsImageRenderer(bounds: bounds)
|
|||
|
|
return renderer.image { rendererContext in
|
|||
|
|
layer.render(in: rendererContext.cgContext)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 获取视图在屏幕上的绝对坐标(相对于整个屏幕)
|
|||
|
|
var screenRect: CGRect? {
|
|||
|
|
guard let window = self.window else { return nil }
|
|||
|
|
return self.superview?.convert(self.frame, to: window)
|
|||
|
|
}
|
|||
|
|
}
|