Epub阅读器0.0.1
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// SSAlertAnimation.swift
|
||||
// SSAlert
|
||||
//
|
||||
// Created by yangsq on 2021/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
public typealias PanProgress = CGFloat
|
||||
public typealias PanCancelProgress = CGFloat
|
||||
public typealias DimissIsCancel = Bool
|
||||
public protocol SSAlertAnimation {
|
||||
/// 动画时间
|
||||
func animationDuration() -> TimeInterval
|
||||
/// 展示动画
|
||||
func showAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion:(((Bool) -> Void))?)
|
||||
/// 隐藏动画
|
||||
func hideAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion:(((Bool) -> Bool))?)
|
||||
/// 刷新大小动画
|
||||
func refreshAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion:(((Bool) -> Void))?)
|
||||
/// 拖拽隐藏动画,当是模态视图弹窗才有
|
||||
func panToDimissTransilatePoint(point: CGPoint, panViewFrame: CGRect) -> (PanProgress, PanCancelProgress)
|
||||
}
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// SSAlertAnimationController.swift
|
||||
// SSAlert
|
||||
//
|
||||
// Created by yangsq on 2021/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
class SSAlertAnimationController: UIViewController {
|
||||
var isDimiss: Bool = false
|
||||
var isHideStatusBar = false {
|
||||
didSet {
|
||||
setNeedsStatusBarAppearanceUpdate()
|
||||
}
|
||||
}
|
||||
var isShowNavWhenViewWillDisappear = true
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .clear
|
||||
}
|
||||
|
||||
override var prefersStatusBarHidden: Bool {
|
||||
return isHideStatusBar
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
navigationController?.setNavigationBarHidden(true, animated: animated)
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
if !isDimiss, isShowNavWhenViewWillDisappear {
|
||||
navigationController?.setNavigationBarHidden(false, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
super.dismiss(animated: flag, completion: completion)
|
||||
isDimiss = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
//
|
||||
// SSAlertCommonView.swift
|
||||
// SSAlert
|
||||
//
|
||||
// Created by yangsq on 2021/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension SSAlertAction {
|
||||
enum Style {
|
||||
case alert
|
||||
case actionSheet
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class SSAlertAction: NSObject {
|
||||
public var title: String
|
||||
public var titleFont = UIFont.systemFont(ofSize: 16)
|
||||
public var titleColor: UIColor? = nil
|
||||
public var height: CGFloat = 55
|
||||
public var backgroundColor: UIColor? = nil
|
||||
public private(set) var style: Style
|
||||
private var addAction:(() -> Void)? = nil
|
||||
public init(style: Style, title: String, addAction: (() -> Void)? = nil) {
|
||||
self.style = style
|
||||
self.title = title
|
||||
self.addAction = addAction
|
||||
if style == .alert {
|
||||
height = 50
|
||||
}
|
||||
super.init()
|
||||
}
|
||||
func triggerAction() {
|
||||
if let addAction = addAction {
|
||||
addAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension SSAlertCommonView {
|
||||
public static var alertMaxWidth = UIScreen.main.bounds.width * 0.7
|
||||
public static var actionSheetMaxWidth = UIScreen.main.bounds.width
|
||||
public static var buttonSpace: CGFloat = 0.5
|
||||
|
||||
public static var safeAreaBottomMargin: CGFloat {
|
||||
if #available(iOS 11.0, *) {
|
||||
return UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
public class SSAlertCommonView: UIView {
|
||||
|
||||
public lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont.systemFont(ofSize: 16)
|
||||
label.numberOfLines = 0
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
public lazy var messageLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont.systemFont(ofSize: 14)
|
||||
label.numberOfLines = 0
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
public lazy var containerView: UIView = {
|
||||
let view = UIView()
|
||||
return view
|
||||
}()
|
||||
|
||||
public lazy var buttonView: UIView = {
|
||||
let view = UIView()
|
||||
return view
|
||||
}()
|
||||
|
||||
public var lineColor: UIColor? = nil {
|
||||
didSet {
|
||||
lineArray.forEach { lineView in
|
||||
lineView.backgroundColor = lineColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var lineEdgeInsets: UIEdgeInsets = .zero {
|
||||
didSet {
|
||||
refreshFrame()
|
||||
}
|
||||
}
|
||||
public private(set) var style: SSAlertAction.Style
|
||||
public private(set) var actions: [SSAlertAction]
|
||||
public private(set) var title: String?
|
||||
public private(set) var message: String?
|
||||
|
||||
private var buttonArray: [UIButton] = []
|
||||
private var lineArray: [UIView] = []
|
||||
private var hasText = false
|
||||
|
||||
|
||||
public init(title: String? = nil, message: String? = nil, style: SSAlertAction.Style, actions:[SSAlertAction] = []) {
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.style = style
|
||||
self.actions = actions
|
||||
super.init(frame: .zero)
|
||||
makeUI()
|
||||
}
|
||||
|
||||
private func makeUI() {
|
||||
addSubview(containerView)
|
||||
if let title = title, !title.isEmpty {
|
||||
titleLabel.text = title
|
||||
containerView.addSubview(titleLabel)
|
||||
hasText = true
|
||||
}
|
||||
if let message = message, !message.isEmpty {
|
||||
messageLabel.text = message
|
||||
containerView.addSubview(messageLabel)
|
||||
hasText = true
|
||||
}
|
||||
|
||||
if actions.count > 0 {
|
||||
addSubview(buttonView)
|
||||
actions.forEach { action in
|
||||
let button = UIButton(type: .system)
|
||||
button.setTitle(action.title, for: .normal)
|
||||
button.backgroundColor = backgroundColor
|
||||
if let backgroundColor = action.backgroundColor {
|
||||
button.backgroundColor = backgroundColor
|
||||
}
|
||||
button.titleLabel?.font = action.titleFont
|
||||
button.setTitleColor(action.titleColor, for: .normal)
|
||||
button.addTarget(self, action: #selector(buttonAction(button:)), for: .touchUpInside)
|
||||
buttonView.addSubview(button)
|
||||
buttonArray.append(button)
|
||||
var isAddLineView = false
|
||||
if style == .actionSheet {
|
||||
if hasText {
|
||||
isAddLineView = true
|
||||
} else {
|
||||
if actions[actions.count - 1] != action {
|
||||
isAddLineView = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if actions.count > 2 {
|
||||
if hasText {
|
||||
isAddLineView = true
|
||||
} else {
|
||||
if actions[actions.count - 1] != action {
|
||||
isAddLineView = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if actions.count == 1 {
|
||||
if hasText {
|
||||
isAddLineView = true
|
||||
}
|
||||
} else {
|
||||
if hasText {
|
||||
isAddLineView = true
|
||||
} else {
|
||||
if actions[actions.count - 1] != action {
|
||||
isAddLineView = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if isAddLineView {
|
||||
let linView = UIView()
|
||||
linView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
|
||||
buttonView.addSubview(linView)
|
||||
lineArray.append(linView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshFrame()
|
||||
}
|
||||
|
||||
private func refreshFrame() {
|
||||
let space: CGFloat = 15
|
||||
let viewMaxWidth = style == .alert ? SSAlertCommonView.alertMaxWidth : SSAlertCommonView.actionSheetMaxWidth
|
||||
var height: CGFloat = 0
|
||||
let width = viewMaxWidth
|
||||
let containerWidth = viewMaxWidth - space * 2
|
||||
var originY: CGFloat = 0
|
||||
var cotainerOriginY: CGFloat = 0
|
||||
if titleLabel.superview != nil {
|
||||
let titleSize = titleLabel.sizeThatFits(CGSize(width: containerWidth, height: CGFloat(MAXFLOAT)))
|
||||
titleLabel.ss_origin = CGPoint(x: 0, y: cotainerOriginY)
|
||||
titleLabel.ss_size = CGSize(width: containerWidth, height: titleSize.height)
|
||||
height += (titleSize.height + space)
|
||||
cotainerOriginY += (space + titleSize.height)
|
||||
originY = space
|
||||
}
|
||||
|
||||
if messageLabel.superview != nil {
|
||||
let messageSize = messageLabel.sizeThatFits(CGSize(width: containerWidth, height: CGFloat(MAXFLOAT)))
|
||||
messageLabel.ss_origin = CGPoint(x: 0, y: cotainerOriginY)
|
||||
messageLabel.ss_size = CGSize(width: containerWidth, height: messageSize.height)
|
||||
height += (messageSize.height + space)
|
||||
cotainerOriginY += (space + messageSize.height)
|
||||
originY = space
|
||||
}
|
||||
|
||||
containerView.ss_origin = CGPoint(x: space, y: originY)
|
||||
containerView.ss_size = CGSize(width: containerWidth, height: height)
|
||||
originY += height
|
||||
|
||||
if !buttonArray.isEmpty {
|
||||
let buttonSpace: CGFloat = SSAlertCommonView.buttonSpace
|
||||
var buttonTotalHeight: CGFloat = 0
|
||||
var buttonTopY: CGFloat = 0
|
||||
if style == .actionSheet {
|
||||
for index in 0..<buttonArray.count {
|
||||
let action = actions[index]
|
||||
let button = buttonArray[index]
|
||||
if lineArray.count == buttonArray.count {
|
||||
let lineView = lineArray[index]
|
||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
||||
buttonTopY += buttonSpace
|
||||
}
|
||||
|
||||
if lineArray.count == buttonArray.count - 1 {
|
||||
if index < lineArray.count {
|
||||
let lineView = lineArray[index]
|
||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY + action.height, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
||||
buttonTopY += buttonSpace
|
||||
}
|
||||
}
|
||||
|
||||
if index == buttonArray.count - 1 {
|
||||
button.ss_size = CGSize(width: width, height: action.height + SSAlertCommonView.safeAreaBottomMargin)
|
||||
button.titleEdgeInsets = UIEdgeInsets(top: -SSAlertCommonView.safeAreaBottomMargin, left: 0, bottom: 0, right: 0)
|
||||
} else {
|
||||
button.ss_size = CGSize(width: width, height: action.height)
|
||||
}
|
||||
button.ss_origin = CGPoint(x: 0, y: buttonTopY)
|
||||
buttonTopY += button.ss_h
|
||||
}
|
||||
buttonTotalHeight = buttonTopY
|
||||
}
|
||||
|
||||
if style == .alert {
|
||||
if buttonArray.count > 2 {
|
||||
buttonArray.forEach { button in
|
||||
let index = buttonArray.firstIndex(of: button)
|
||||
let action = actions[index!]
|
||||
|
||||
if lineArray.count == buttonArray.count {
|
||||
let lineView = lineArray[index!]
|
||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
||||
buttonTopY += buttonSpace
|
||||
}
|
||||
if lineArray.count == buttonArray.count - 1 {
|
||||
if index! < lineArray.count {
|
||||
let lineView = lineArray[index!]
|
||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY + action.height, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
||||
buttonTopY += buttonSpace
|
||||
}
|
||||
}
|
||||
|
||||
button.ss_origin = CGPoint(x: 0, y: buttonTopY)
|
||||
button.ss_size = CGSize(width: width, height: action.height)
|
||||
buttonTopY += action.height
|
||||
}
|
||||
buttonTotalHeight = buttonTopY
|
||||
} else {
|
||||
if buttonArray.count == 1 {
|
||||
let button = buttonArray.first!
|
||||
let action = actions.first!
|
||||
if lineArray.count > 0 {
|
||||
let lineView = lineArray.first!
|
||||
lineView.frame = CGRect(x: lineEdgeInsets.left, y: buttonTopY, width: width - lineEdgeInsets.left - lineEdgeInsets.right, height: buttonSpace)
|
||||
buttonTopY += buttonSpace
|
||||
}
|
||||
button.ss_origin = CGPoint(x: 0, y: buttonTopY)
|
||||
button.ss_size = CGSize(width: width, height: action.height)
|
||||
buttonTotalHeight = buttonTopY
|
||||
} else {
|
||||
let button1 = buttonArray.first!
|
||||
let button2 = buttonArray.last!
|
||||
let action1 = actions.first!
|
||||
let action2 = actions.last!
|
||||
let maxButtonHeight = max(action1.height, action2.height)
|
||||
if hasText {
|
||||
let lineView1 = lineArray.first!
|
||||
lineView1.frame = CGRect(x: 0, y: buttonTopY, width: width, height: buttonSpace)
|
||||
buttonTopY += buttonSpace
|
||||
}
|
||||
|
||||
button1.ss_origin = CGPoint(x: 0, y: buttonTopY)
|
||||
button1.ss_size = CGSize(width: width / 2 - buttonSpace / 2, height: maxButtonHeight)
|
||||
|
||||
|
||||
button2.ss_origin = CGPoint(x: button1.frame.maxX + buttonSpace, y: buttonTopY)
|
||||
button2.ss_size = CGSize(width: width / 2 - buttonSpace / 2, height: maxButtonHeight)
|
||||
|
||||
let lineView2 = lineArray.last!
|
||||
lineView2.frame = CGRect(x: button1.frame.maxX, y: buttonSpace, width: buttonSpace, height: button1.ss_h)
|
||||
buttonTopY += maxButtonHeight
|
||||
}
|
||||
buttonTotalHeight = buttonTopY
|
||||
}
|
||||
}
|
||||
|
||||
buttonView.ss_origin = CGPoint(x: 0, y: originY)
|
||||
buttonView.ss_size = CGSize(width: width, height: buttonTotalHeight)
|
||||
height += buttonTotalHeight + (originY > 0 ? space : 0)
|
||||
|
||||
}
|
||||
self.ss_size = CGSize(width: width, height: height)
|
||||
}
|
||||
|
||||
@objc private func buttonAction(button: UIButton) {
|
||||
let index = buttonArray.firstIndex(of: button)
|
||||
let action = actions[index!]
|
||||
action.triggerAction()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
//
|
||||
// SSAlertDefaultAnmation.swift
|
||||
// SSAlert
|
||||
//
|
||||
// Created by yangsq on 2021/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
extension SSAlertDefaultAnmation {
|
||||
public enum State {
|
||||
case fromTop, fromLeft, fromBottom, fromRight, fromCenter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open class SSAlertDefaultAnmation: NSObject {
|
||||
public var state: State
|
||||
/// 中心点偏移原来位置位移
|
||||
public var centerOffset: CGPoint = .zero
|
||||
/// 动画的时间
|
||||
public var duration: TimeInterval = 0.3
|
||||
/// 展示动画是否开启弹簧效果
|
||||
public var isSpringShowAnimation: Bool = true
|
||||
/// 滑动消失距离触发
|
||||
public var panDimissProgress: CGFloat = 0.4
|
||||
public var usingSpringWithDamping: CGFloat = 0.8
|
||||
public var initialSpringVelocity: CGFloat = 0.5
|
||||
private weak var animationView: SSAlertView? = nil
|
||||
public init(state: State) {
|
||||
self.state = state
|
||||
super.init()
|
||||
}
|
||||
|
||||
private func animation(animated: Bool, isSpringAnimation: Bool = true, animations: @escaping (() -> Void), completion: ((Bool) -> Void)? = nil) {
|
||||
if animated {
|
||||
if isSpringAnimation {
|
||||
UIView.animate(withDuration: animationDuration(),
|
||||
delay: 0,
|
||||
usingSpringWithDamping: usingSpringWithDamping,
|
||||
initialSpringVelocity: initialSpringVelocity,
|
||||
options: [],
|
||||
animations: animations,
|
||||
completion: completion)
|
||||
} else {
|
||||
UIView.animate(withDuration: animationDuration(),
|
||||
animations: animations,
|
||||
completion: completion)
|
||||
}
|
||||
|
||||
} else {
|
||||
animations()
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func setCenterOffset(center offset: CGPoint, duration: TimeInterval = 0.3, animated: Bool = true, completion:((Bool) -> Void)?) {
|
||||
self.duration = duration
|
||||
self.animation(animated: animated, animations: {
|
||||
self.animationView?.transform = CGAffineTransform.init(translationX: offset.x, y: offset.y)
|
||||
}, completion: completion)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension SSAlertDefaultAnmation: SSAlertAnimation {
|
||||
public func animationDuration() -> TimeInterval {
|
||||
return duration
|
||||
}
|
||||
|
||||
public func showAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion: (((Bool) -> Void))?) {
|
||||
guard let animationSuperView = animationView.superview else {
|
||||
return
|
||||
}
|
||||
self.animationView = animationView
|
||||
animationView.alpha = 1
|
||||
animationView.ss_size = viewSize
|
||||
switch state {
|
||||
case .fromBottom:
|
||||
animationView.ss_centerX = animationSuperView.ss_w / 2 + centerOffset.x
|
||||
animationView.ss_y = animationSuperView.ss_h
|
||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: { [weak self] in guard let self = self else { return }
|
||||
animationView.transform = CGAffineTransform.init(translationX: 0, y: -animationView.ss_h + self.centerOffset.y)
|
||||
animationView.backgroundMask?.alpha = 1;
|
||||
}, completion: completion)
|
||||
|
||||
case .fromTop:
|
||||
animationView.ss_centerX = animationSuperView.ss_w / 2 + centerOffset.x
|
||||
animationView.ss_y = -animationView.ss_h
|
||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: { [weak self] in guard let self = self else { return }
|
||||
animationView.transform = CGAffineTransform.init(translationX: 0, y: animationView.ss_h + self.centerOffset.y)
|
||||
animationView.backgroundMask?.alpha = 1;
|
||||
}, completion: completion)
|
||||
|
||||
case .fromLeft:
|
||||
animationView.ss_centerY = animationSuperView.ss_h / 2 + centerOffset.y
|
||||
animationView.ss_x = -animationView.ss_w
|
||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: {
|
||||
animationView.transform = CGAffineTransform.init(translationX: animationView.ss_w + self.centerOffset.x, y: 0)
|
||||
animationView.backgroundMask?.alpha = 1;
|
||||
}, completion: completion)
|
||||
|
||||
case .fromRight:
|
||||
animationView.ss_centerY = animationSuperView.ss_h / 2 + centerOffset.y
|
||||
animationView.ss_x = animationSuperView.ss_w
|
||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: {
|
||||
animationView.transform = CGAffineTransform.init(translationX: -animationView.ss_w + self.centerOffset.x, y: 0)
|
||||
animationView.backgroundMask?.alpha = 1;
|
||||
}, completion: completion)
|
||||
|
||||
case .fromCenter:
|
||||
animationView.ss_centerX = animationSuperView.ss_w / 2 + centerOffset.x
|
||||
animationView.ss_centerY = animationSuperView.ss_h / 2 + centerOffset.y
|
||||
animationView.transform = CGAffineTransform.init(scaleX: 1.5, y: 1.5)
|
||||
animationView.alpha = 0
|
||||
animation(animated: animated, isSpringAnimation: isSpringShowAnimation, animations: {
|
||||
animationView.alpha = 1
|
||||
animationView.transform = CGAffineTransform.identity
|
||||
animationView.backgroundMask?.alpha = 1;
|
||||
}, completion: completion)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public func hideAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion: (((Bool) -> Bool))?) {
|
||||
self.animationView = animationView
|
||||
animation(animated: animated, isSpringAnimation: false, animations: {
|
||||
if self.state == .fromCenter {
|
||||
animationView.alpha = 0
|
||||
} else {
|
||||
animationView.transform = CGAffineTransform.identity
|
||||
}
|
||||
animationView.backgroundMask?.alpha = 0
|
||||
}, completion: {
|
||||
finished in
|
||||
var isCancel = false
|
||||
if let completion = completion {
|
||||
isCancel = completion(finished)
|
||||
}
|
||||
if !isCancel {
|
||||
animationView.removeFromSuperview()
|
||||
animationView.backgroundMask?.removeFromSuperview()
|
||||
self.animationView = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func refreshAnimationOfAnimationView(animationView: SSAlertView, viewSize: CGSize, animated: Bool, completion: (((Bool) -> Void))?) {
|
||||
guard let animationSuperView = animationView.superview else {
|
||||
return
|
||||
}
|
||||
self.animationView = animationView
|
||||
animationView.alpha = 1
|
||||
animationView.ss_size = viewSize
|
||||
switch state {
|
||||
case .fromBottom:
|
||||
animationView.ss_centerX = animationSuperView.ss_w / 2.0 + centerOffset.x
|
||||
animation(animated: animated, animations: {
|
||||
animationView.ss_y = animationSuperView.ss_h - viewSize.height + self.centerOffset.y
|
||||
}, completion: completion)
|
||||
|
||||
case .fromTop:
|
||||
animationView.ss_centerX = animationSuperView.ss_w / 2.0 + centerOffset.x
|
||||
animation(animated: animated, animations: {
|
||||
animationView.ss_y = self.centerOffset.y
|
||||
}, completion: completion)
|
||||
|
||||
case .fromLeft:
|
||||
animationView.ss_centerY = animationSuperView.ss_h / 2.0 + centerOffset.y
|
||||
animation(animated: animated, animations: {
|
||||
animationView.ss_x = self.centerOffset.x
|
||||
}, completion: completion)
|
||||
|
||||
case .fromRight:
|
||||
animationView.ss_centerY = animationSuperView.ss_h / 2.0 + centerOffset.y
|
||||
animation(animated: animated, animations: {
|
||||
animationView.ss_x = animationSuperView.ss_w - viewSize.width + self.centerOffset.x
|
||||
}, completion: completion)
|
||||
|
||||
case .fromCenter:
|
||||
animationView.alpha = 0
|
||||
animationView.ss_centerX = animationSuperView.ss_w / 2 + centerOffset.x
|
||||
animationView.ss_centerY = animationSuperView.ss_h / 2 + centerOffset.y
|
||||
animationView.transform = CGAffineTransform.init(scaleX: 1.5, y: 1.5)
|
||||
animation(animated: animated, animations: {
|
||||
animationView.alpha = 1
|
||||
animationView.transform = CGAffineTransform.identity
|
||||
}, completion: completion)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public func panToDimissTransilatePoint(point: CGPoint, panViewFrame: CGRect) -> (PanProgress, PanCancelProgress) {
|
||||
var progress: CGFloat = 0
|
||||
switch state {
|
||||
case .fromTop:
|
||||
if point.y <= 0 {
|
||||
progress = abs(point.y / panViewFrame.height)
|
||||
}
|
||||
case .fromBottom:
|
||||
if point.y >= 0 {
|
||||
progress = abs(point.y / panViewFrame.height)
|
||||
}
|
||||
|
||||
case .fromLeft:
|
||||
if point.x <= 0 {
|
||||
progress = abs(point.x / panViewFrame.width)
|
||||
}
|
||||
|
||||
case .fromRight:
|
||||
if point.x >= 0 {
|
||||
progress = abs(point.x / panViewFrame.width)
|
||||
}
|
||||
case .fromCenter:
|
||||
progress = abs(point.y / panViewFrame.height)
|
||||
}
|
||||
return (progress, panDimissProgress)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// SSAlertPresentAnimation.swift
|
||||
// SSAlert
|
||||
//
|
||||
// Created by yangsq on 2021/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias PanDimissAnimation = (CGPoint, CGRect) -> (PanProgress, PanCancelProgress)
|
||||
typealias DimissAnimation = (DimissIsCancel) -> Void
|
||||
|
||||
class SSCustomInteractiveAnimation: UIPercentDrivenInteractiveTransition {
|
||||
private(set) var panGestureRecognizer: UIPanGestureRecognizer
|
||||
private var panDimissAnimation: PanDimissAnimation?
|
||||
private var dimissAnimation: DimissAnimation?
|
||||
|
||||
init(panGestureRecognizer: UIPanGestureRecognizer, panDimissAnimation: PanDimissAnimation?, dimissAnimation: DimissAnimation?) {
|
||||
self.panGestureRecognizer = panGestureRecognizer
|
||||
self.panDimissAnimation = panDimissAnimation
|
||||
self.dimissAnimation = dimissAnimation
|
||||
super.init()
|
||||
self.completionSpeed = 0.2
|
||||
panGestureRecognizer.addTarget(self, action: #selector(panAction(pan:)))
|
||||
}
|
||||
|
||||
@objc private func panAction(pan: UIPanGestureRecognizer) {
|
||||
guard let view = pan.view else {
|
||||
return
|
||||
}
|
||||
let poin = pan.translation(in: view)
|
||||
var progress: CGFloat = 0
|
||||
var cancelProgress: CGFloat = 0.4
|
||||
if let panDimissAnimation = self.panDimissAnimation {
|
||||
let (tProgress, tCancelProgress) = panDimissAnimation(poin, pan.view!.frame)
|
||||
progress = tProgress
|
||||
cancelProgress = tCancelProgress
|
||||
}
|
||||
switch pan.state {
|
||||
case .began:
|
||||
pan.setTranslation(CGPoint(x: 0, y: 0), in: view)
|
||||
case .changed:
|
||||
update(progress)
|
||||
case .cancelled:
|
||||
cancel()
|
||||
case .ended:
|
||||
if progress > cancelProgress {
|
||||
finish()
|
||||
dimissAnimation?(false)
|
||||
} else {
|
||||
cancel()
|
||||
dimissAnimation?(true)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
|
||||
super.startInteractiveTransition(transitionContext)
|
||||
}
|
||||
|
||||
deinit {
|
||||
panGestureRecognizer.removeTarget(self, action: #selector(panAction(pan:)))
|
||||
}
|
||||
}
|
||||
|
||||
class SSAlertPresentAnimation: NSObject {
|
||||
typealias AnimationCompletion = () -> Void
|
||||
var duration: TimeInterval = 0.35
|
||||
private var showAnimation: AnimationCompletion? = nil
|
||||
private var hideAnimation: AnimationCompletion? = nil
|
||||
private var endCompletion: AnimationCompletion? = nil
|
||||
private var panDimissAnimation: PanDimissAnimation? = nil
|
||||
private var dimissAnimation: DimissAnimation? = nil
|
||||
|
||||
private var transitionContext: UIViewControllerContextTransitioning?
|
||||
private weak var nav: UINavigationController?
|
||||
private weak var animationView: UIView?
|
||||
private var panGestureRecognizer: UIPanGestureRecognizer?
|
||||
|
||||
init(navigationViewController: UINavigationController, animationView: UIView, canPanDimiss: Bool = true) {
|
||||
self.nav = navigationViewController
|
||||
self.animationView = animationView
|
||||
super.init()
|
||||
if canPanDimiss {
|
||||
let pan = UIPanGestureRecognizer(target: self, action: #selector(panAction(pan:)))
|
||||
pan.maximumNumberOfTouches = 1
|
||||
animationView.addGestureRecognizer(pan)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func panAction(pan: UIPanGestureRecognizer) {
|
||||
if pan.state == .began {
|
||||
panGestureRecognizer = pan
|
||||
nav?.viewControllers[0].dismiss(animated: true, completion: {
|
||||
[weak self] in guard let self = self else { return }
|
||||
self.panGestureRecognizer = nil
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
func animationCompletion(showAnimation: AnimationCompletion? = nil,
|
||||
hideAnimation: AnimationCompletion? = nil,
|
||||
endCompletion: AnimationCompletion? = nil,
|
||||
panDimissAnimation: PanDimissAnimation? = nil,
|
||||
dimissAnimation: DimissAnimation? = nil) {
|
||||
if showAnimation != nil {
|
||||
self.showAnimation = showAnimation
|
||||
}
|
||||
if hideAnimation != nil {
|
||||
self.hideAnimation = hideAnimation
|
||||
}
|
||||
|
||||
if endCompletion != nil {
|
||||
self.endCompletion = endCompletion
|
||||
}
|
||||
if panDimissAnimation != nil {
|
||||
self.panDimissAnimation = panDimissAnimation
|
||||
}
|
||||
if panDimissAnimation != nil {
|
||||
self.panDimissAnimation = panDimissAnimation
|
||||
}
|
||||
|
||||
if dimissAnimation != nil {
|
||||
self.dimissAnimation = dimissAnimation
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func setCompleteTransitionIsHide(isHide: Bool) -> Bool {
|
||||
if let transitionContext = transitionContext {
|
||||
let isCancelled = transitionContext.transitionWasCancelled
|
||||
if !isCancelled {
|
||||
if isHide {
|
||||
transitionContext.containerView.subviews.forEach { subView in
|
||||
subView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
transitionContext.completeTransition(!isCancelled)
|
||||
return isCancelled
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension SSAlertPresentAnimation: UIViewControllerTransitioningDelegate {
|
||||
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return self
|
||||
}
|
||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return self
|
||||
}
|
||||
|
||||
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
if self.panGestureRecognizer != nil {
|
||||
return SSCustomInteractiveAnimation(panGestureRecognizer: self.panGestureRecognizer!, panDimissAnimation: self.panDimissAnimation, dimissAnimation: dimissAnimation)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
if self.panGestureRecognizer != nil {
|
||||
|
||||
return SSCustomInteractiveAnimation(panGestureRecognizer: self.panGestureRecognizer!, panDimissAnimation: self.panDimissAnimation, dimissAnimation: dimissAnimation)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension SSAlertPresentAnimation: UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return self.duration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
self.transitionContext = transitionContext
|
||||
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
|
||||
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
|
||||
let containerView = transitionContext.containerView
|
||||
|
||||
if let fromVC = fromVC, let toVC = toVC {
|
||||
var fromView = fromVC.view
|
||||
var toView = toVC.view
|
||||
if transitionContext.responds(to: #selector(UIViewControllerContextTransitioning.view(forKey:))) {
|
||||
fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)
|
||||
toView = transitionContext.view(forKey: UITransitionContextViewKey.to)
|
||||
}
|
||||
let isPresenting = toVC.presentingViewController == fromVC
|
||||
if isPresenting {
|
||||
if let toView = toView {
|
||||
containerView.addSubview(toView)
|
||||
|
||||
}
|
||||
if let showAnimation = self.showAnimation {
|
||||
showAnimation()
|
||||
}
|
||||
} else {
|
||||
if let toView = toView, let fromView = fromView {
|
||||
containerView.insertSubview(toView, belowSubview: fromView)
|
||||
|
||||
}
|
||||
if let hideAnimation = self.hideAnimation {
|
||||
hideAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
func animationEnded(_ transitionCompleted: Bool) {
|
||||
if let endCompletion = self.endCompletion, transitionCompleted, let transitionContext = transitionContext, transitionContext.isInteractive {
|
||||
endCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
//
|
||||
// SSAlertView.swift
|
||||
// SSAlert
|
||||
//
|
||||
// Created by yangsq on 2021/8/13.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension SSAlertView {
|
||||
public enum BackgroundMaskType {
|
||||
/// 默认没有遮罩
|
||||
case none
|
||||
|
||||
/// 透明遮罩
|
||||
case clear
|
||||
|
||||
/// 黑色遮罩
|
||||
case black
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension SSAlertView {
|
||||
typealias HideCompletion = () -> Void
|
||||
class HideCompletionData: NSObject {
|
||||
var completion: HideCompletion?
|
||||
}
|
||||
}
|
||||
|
||||
open class SSAlertView: UIView {
|
||||
/// customView 的内间距
|
||||
public var customEdgeInsets: UIEdgeInsets = .zero {
|
||||
didSet {
|
||||
setNeedsDisplay()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
/// 展示和隐藏动画设置
|
||||
public var animation: SSAlertAnimation
|
||||
|
||||
///遮罩视图
|
||||
public private(set) var backgroundMask: UIView?
|
||||
|
||||
/// 是否点击遮罩隐藏,默认点击会隐藏
|
||||
public var canTouchMaskHide: Bool = true
|
||||
/// 点击遮罩隐藏是否开启动画,默认nil,跟随展示的时候设置是否开启动画
|
||||
public var isTouchMaskHideAnimated: Bool? = nil
|
||||
|
||||
/// 模态视图弹窗才有
|
||||
public private(set) var navigationController: UINavigationController?
|
||||
|
||||
public var isHideStatusBar = false {
|
||||
didSet {
|
||||
self.toViewContrller?.isHideStatusBar = isHideStatusBar
|
||||
}
|
||||
}
|
||||
public var isShowNavWhenViewWillDisappear = true {
|
||||
didSet {
|
||||
self.toViewContrller?.isShowNavWhenViewWillDisappear = isShowNavWhenViewWillDisappear
|
||||
}
|
||||
}
|
||||
private var onView: UIView
|
||||
public private(set) var maskType: BackgroundMaskType
|
||||
public private(set) var customView: UIView
|
||||
private weak var fromViewController: UIViewController?
|
||||
private weak var toViewContrller: SSAlertAnimationController?
|
||||
private var presentAnimation: SSAlertPresentAnimation?
|
||||
private var canPanDimiss: Bool = false
|
||||
private var hideCompletions = [HideCompletionData]()
|
||||
/// 初始化(普通视图弹窗)
|
||||
public init(customView: UIView,
|
||||
onView: UIView,
|
||||
animation: SSAlertAnimation = SSAlertDefaultAnmation(state: .fromCenter),
|
||||
maskType: BackgroundMaskType = .black) {
|
||||
self.animation = animation
|
||||
self.onView = onView
|
||||
self.maskType = maskType
|
||||
self.customView = customView
|
||||
super.init(frame: .zero)
|
||||
makeUI()
|
||||
}
|
||||
|
||||
/// 初始化(模态视图弹窗),canPanDimiss: 是否支持拖拽消息
|
||||
public convenience init(customView: UIView,
|
||||
fromViewController: UIViewController,
|
||||
animation: SSAlertAnimation = SSAlertDefaultAnmation(state: .fromCenter),
|
||||
navigationControllerClass: UINavigationController.Type = UINavigationController.self,
|
||||
maskType: BackgroundMaskType = .black,
|
||||
canPanDimiss: Bool = true) {
|
||||
let animaionVC = SSAlertAnimationController()
|
||||
self.init(customView: customView, onView: animaionVC.view, animation: animation, maskType: maskType)
|
||||
animaionVC.isHideStatusBar = self.isHideStatusBar
|
||||
if let superViewController = superViewController(view: customView), superViewController != animaionVC {
|
||||
animaionVC.addChild(superViewController)
|
||||
superViewController.didMove(toParent: animaionVC)
|
||||
}
|
||||
self.canPanDimiss = canPanDimiss
|
||||
self.fromViewController = fromViewController
|
||||
let nav = navigationControllerClass.init(rootViewController: animaionVC)
|
||||
nav.modalPresentationStyle = .custom
|
||||
self.navigationController = nav
|
||||
self.canPanDimiss = canPanDimiss
|
||||
let animation = SSAlertPresentAnimation(navigationViewController: nav, animationView: customView, canPanDimiss: canPanDimiss)
|
||||
self.navigationController!.transitioningDelegate = animation
|
||||
self.navigationController!.modalPresentationCapturesStatusBarAppearance = true
|
||||
self.presentAnimation = animation
|
||||
self.toViewContrller = animaionVC
|
||||
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
backgroundMask?.frame = CGRect(x: 0, y: 0, width: onView.ss_w, height: onView.ss_h)
|
||||
customView.ss_x = customEdgeInsets.left
|
||||
customView.ss_y = customEdgeInsets.top
|
||||
customView.ss_w = frame.width - customEdgeInsets.left - customEdgeInsets.right
|
||||
customView.ss_h = frame.height - customEdgeInsets.top - customEdgeInsets.bottom
|
||||
}
|
||||
|
||||
|
||||
private func makeUI() {
|
||||
if maskType != .none {
|
||||
let backgroundMask = UIButton()
|
||||
backgroundMask.alpha = 0
|
||||
onView.addSubview(backgroundMask)
|
||||
backgroundMask.addTarget(self, action: #selector(backgroundMaskAction), for: .touchUpInside)
|
||||
self.backgroundMask = backgroundMask
|
||||
if maskType == .black {
|
||||
backgroundMask.backgroundColor = UIColor.black.withAlphaComponent(0.3)
|
||||
}
|
||||
}
|
||||
addSubview(customView)
|
||||
onView.addSubview(self)
|
||||
}
|
||||
/// 展示
|
||||
public func show(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
if navigationController != nil {
|
||||
presentView(animated: animated, completion: completion)
|
||||
} else {
|
||||
showView(animated: animated, completion: completion)
|
||||
}
|
||||
}
|
||||
/// 隐藏
|
||||
public func hide(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
if navigationController != nil {
|
||||
dimissView(animated: animated, completion: completion)
|
||||
} else {
|
||||
hideView(animated: animated, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
/// 展示(普通视图弹窗)
|
||||
/// - Parameter animated: 是否动画
|
||||
private func showView(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
isTouchMaskHideAnimated = animated
|
||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
||||
if customView.ss_size != .zero {
|
||||
customSize = customView.ss_size
|
||||
}
|
||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
||||
animation.showAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated, completion: { _ in completion?() })
|
||||
}
|
||||
/// 隐藏(普通视图弹窗)
|
||||
/// - Parameter animated: 是否动画
|
||||
private func hideView(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
||||
if customView.ss_size != .zero {
|
||||
customSize = customView.ss_size
|
||||
}
|
||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
||||
animation.hideAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated) { [weak self] _ in guard let self = self else { return false }
|
||||
completion?()
|
||||
|
||||
self.hideCompletions.forEach { hideC in
|
||||
hideC.completion?()
|
||||
hideC.completion = nil
|
||||
self.hideCompletions.removeAll(where: {$0 == hideC})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
/// 展示(模态视图弹窗)
|
||||
/// - Parameter animated: 是否动画
|
||||
private func presentView(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
isTouchMaskHideAnimated = animated
|
||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
||||
if customView.ss_size != .zero {
|
||||
customSize = customView.ss_size
|
||||
}
|
||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
||||
presentAnimation?.animationCompletion(showAnimation: {
|
||||
[weak self] in guard let self = self else { return }
|
||||
self.animation.showAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated) { _ in
|
||||
self.presentAnimation?.setCompleteTransitionIsHide(isHide: false)
|
||||
completion?()
|
||||
}
|
||||
})
|
||||
self.presentAnimation?.duration = animation.animationDuration()
|
||||
///存在的问题: app当前显示的是弹窗界面,默认presnent出来的视图且modalPresentationStyle不是CurrentContext这这种,这个视图的presentingVC默认根控制器,例如存在tabbarVC的话就是tabbarVC,这个会造成一个问题:当这个视图dimiss的时候,会把所有的presentedVC都dimiss(其实调用dimiss的时候是调用当前视图的presentigngVC去dimiss,遵循随创建随释放)
|
||||
/// 这个通过设置 definesPresentationContext = true,modalPresentationStyle = UIModalPresentationOverCurrentContext,这样当前视图的presentigngVC为 self.navigationController,
|
||||
/// UIModalPresentationOverCurrentContext和 UIModalPresentationCurrentContext的区别是弹出的界面,前者不会把底部的视图stact移除,后者则会。
|
||||
self.navigationController?.definesPresentationContext = true
|
||||
self.navigationController?.modalPresentationStyle = .overCurrentContext
|
||||
self.fromViewController?.present(self.navigationController!, animated: true, completion: nil)
|
||||
if canPanDimiss {
|
||||
dimissViewDragToHideAnimation(animated: animated)
|
||||
}
|
||||
}
|
||||
/// 隐藏(模态视图弹窗)
|
||||
/// - Parameter animated: 是否动画
|
||||
private func dimissView(animated: Bool = true, completion: (() -> Void)? = nil) {
|
||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
||||
if customView.ss_size != .zero {
|
||||
customSize = customView.ss_size
|
||||
}
|
||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
||||
presentAnimation?.animationCompletion(hideAnimation: {
|
||||
[weak self] in guard let self = self else { return }
|
||||
self.animation.hideAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated) { _ in
|
||||
return self.presentAnimation!.setCompleteTransitionIsHide(isHide: true)
|
||||
}
|
||||
})
|
||||
self.presentAnimation?.duration = animation.animationDuration()
|
||||
self.toViewContrller?.dismiss(animated: animated, completion: {
|
||||
[weak self] in guard let self = self else { return }
|
||||
completion?()
|
||||
self.hideCompletions.forEach { hideC in
|
||||
hideC.completion?()
|
||||
hideC.completion = nil
|
||||
self.hideCompletions.removeAll(where: {$0 == hideC})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func dimissViewDragToHideAnimation(animated: Bool) {
|
||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
||||
if customView.ss_size != .zero {
|
||||
customSize = customView.ss_size
|
||||
}
|
||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
||||
presentAnimation?.animationCompletion(hideAnimation: {
|
||||
[weak self] in guard let self = self else { return }
|
||||
self.animation.hideAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated) { _ in
|
||||
return self.presentAnimation!.setCompleteTransitionIsHide(isHide: true)
|
||||
}
|
||||
},endCompletion: {
|
||||
[weak self] in guard let self = self else { return }
|
||||
self.hideCompletions.forEach { hideC in
|
||||
hideC.completion?()
|
||||
hideC.completion = nil
|
||||
self.hideCompletions.removeAll(where: {$0 == hideC})
|
||||
}
|
||||
}, panDimissAnimation: { [weak self] point, frame in
|
||||
guard let self = self else { return (0, 0.4) }
|
||||
return self.animation.panToDimissTransilatePoint(point: point, panViewFrame: frame)
|
||||
}, dimissAnimation: { [weak self] isCancel in guard let self = self else { return }
|
||||
self.toViewContrller?.isDimiss = !isCancel
|
||||
})
|
||||
self.presentAnimation?.duration = animation.animationDuration()
|
||||
}
|
||||
|
||||
public func refreshFrame(animated: Bool = true) {
|
||||
var customSize = customView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
|
||||
if customView.ss_size != .zero {
|
||||
customSize = customView.ss_size
|
||||
}
|
||||
let size = CGSize(width: customSize.width + customEdgeInsets.left + customEdgeInsets.right, height: customSize.height + customEdgeInsets.top + customEdgeInsets.bottom)
|
||||
animation.refreshAnimationOfAnimationView(animationView: self, viewSize: size, animated: animated, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@objc private func backgroundMaskAction() {
|
||||
if canTouchMaskHide {
|
||||
if fromViewController != nil {
|
||||
dimissView(animated: isTouchMaskHideAnimated ?? false)
|
||||
} else {
|
||||
hideView(animated: isTouchMaskHideAnimated ?? false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func superViewController(view: UIView) -> UIViewController? {
|
||||
var next = view.next
|
||||
while next != nil {
|
||||
if next is UIViewController {
|
||||
return next as? UIViewController
|
||||
} else {
|
||||
next = next!.next
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func observeHideCompletion(completion: (() -> Void)?) {
|
||||
let completionData = HideCompletionData()
|
||||
completionData.completion = completion
|
||||
self.hideCompletions.append(completionData)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// SSAlertViewExtention.swift
|
||||
// SSAlert
|
||||
//
|
||||
// Created by yangsq on 2021/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension SSAlertView {
|
||||
@discardableResult
|
||||
public class func alertView(style: SSAlertAction.Style, title: String? = nil, message: String? = nil, cancelButton: String? = nil, otherButtons: [String] = [], onView: UIView, onTrigger:((Int) -> Void)? = nil) -> SSAlertView {
|
||||
var actionArray = [SSAlertAction]()
|
||||
var cancelAction: SSAlertAction? = nil
|
||||
var alertView: SSAlertView?
|
||||
if style == .alert {
|
||||
if let cancelButton = cancelButton, !cancelButton.isEmpty {
|
||||
cancelAction = SSAlertAction(style: style, title: cancelButton, addAction: {
|
||||
if let onTrigger = onTrigger {
|
||||
onTrigger(0)
|
||||
}
|
||||
alertView?.hide()
|
||||
})
|
||||
actionArray.append(cancelAction!)
|
||||
}
|
||||
otherButtons.forEach { title in
|
||||
let action = SSAlertAction(style: style, title: title) {
|
||||
if let onTrigger = onTrigger {
|
||||
let index = otherButtons.firstIndex(of: title)
|
||||
onTrigger(cancelAction != nil ? (index! + 1) : index!)
|
||||
}
|
||||
}
|
||||
actionArray.append(action)
|
||||
}
|
||||
}
|
||||
|
||||
if style == .actionSheet {
|
||||
otherButtons.forEach { title in
|
||||
let action = SSAlertAction(style: style, title: title) {
|
||||
if let onTrigger = onTrigger {
|
||||
let index = otherButtons.firstIndex(of: title)
|
||||
onTrigger(cancelAction != nil ? (index! + 1) : index!)
|
||||
}
|
||||
}
|
||||
actionArray.append(action)
|
||||
}
|
||||
if let cancelButton = cancelButton, !cancelButton.isEmpty {
|
||||
cancelAction = SSAlertAction(style: style, title: cancelButton, addAction: {
|
||||
if let onTrigger = onTrigger {
|
||||
onTrigger(0)
|
||||
}
|
||||
alertView?.hide()
|
||||
})
|
||||
actionArray.append(cancelAction!)
|
||||
}
|
||||
}
|
||||
|
||||
let customView = SSAlertCommonView(title: title, message: message, style: style, actions: actionArray)
|
||||
customView.backgroundColor = .white
|
||||
alertView = SSAlertView(customView: customView, onView: onView, animation: SSAlertDefaultAnmation(state: style == .actionSheet ? .fromBottom : .fromCenter), maskType: .black)
|
||||
alertView!.canTouchMaskHide = false
|
||||
if style == .alert {
|
||||
alertView!.layer.cornerRadius = 10
|
||||
alertView!.layer.masksToBounds = true
|
||||
}
|
||||
return alertView!
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public class func modalAlertView(style: SSAlertAction.Style, title: String? = nil, message: String? = nil, cancelButton: String? = nil, otherButtons: [String] = [], fromViewController: UIViewController, navigationViewControllerClass: UINavigationController.Type = UINavigationController.self, onTrigger:((Int) -> Void)? = nil) -> SSAlertView {
|
||||
var actionArray = [SSAlertAction]()
|
||||
var cancelAction: SSAlertAction? = nil
|
||||
var alertView: SSAlertView?
|
||||
if style == .alert {
|
||||
if let cancelButton = cancelButton, !cancelButton.isEmpty {
|
||||
cancelAction = SSAlertAction(style: style, title: cancelButton, addAction: {
|
||||
if let onTrigger = onTrigger {
|
||||
onTrigger(0)
|
||||
}
|
||||
alertView?.hide()
|
||||
})
|
||||
actionArray.append(cancelAction!)
|
||||
}
|
||||
otherButtons.forEach { title in
|
||||
let action = SSAlertAction(style: style, title: title) {
|
||||
if let onTrigger = onTrigger {
|
||||
let index = otherButtons.firstIndex(of: title)
|
||||
onTrigger(cancelAction != nil ? (index! + 1) : index!)
|
||||
}
|
||||
}
|
||||
actionArray.append(action)
|
||||
}
|
||||
}
|
||||
|
||||
if style == .actionSheet {
|
||||
otherButtons.forEach { title in
|
||||
let action = SSAlertAction(style: style, title: title) {
|
||||
if let onTrigger = onTrigger {
|
||||
let index = otherButtons.firstIndex(of: title)
|
||||
onTrigger(cancelAction != nil ? (index! + 1) : index!)
|
||||
}
|
||||
}
|
||||
actionArray.append(action)
|
||||
}
|
||||
if let cancelButton = cancelButton, !cancelButton.isEmpty {
|
||||
cancelAction = SSAlertAction(style: style, title: cancelButton, addAction: {
|
||||
if let onTrigger = onTrigger {
|
||||
onTrigger(0)
|
||||
}
|
||||
alertView?.hide()
|
||||
})
|
||||
actionArray.append(cancelAction!)
|
||||
}
|
||||
}
|
||||
let customView = SSAlertCommonView(title: title, message: message, style: style, actions: actionArray)
|
||||
customView.backgroundColor = .white
|
||||
alertView = SSAlertView(customView: customView,fromViewController: fromViewController, animation: SSAlertDefaultAnmation(state: style == .actionSheet ? .fromBottom : .fromCenter), navigationControllerClass: navigationViewControllerClass, maskType: .black)
|
||||
alertView!.canTouchMaskHide = false
|
||||
if style == .alert {
|
||||
alertView!.layer.cornerRadius = 10
|
||||
alertView!.layer.masksToBounds = true
|
||||
}
|
||||
return alertView!
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// UIViewFrameExtension.swift
|
||||
// Pods-SSAlertSwiftDemo
|
||||
//
|
||||
// Created by yangsq on 2021/8/13.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIView {
|
||||
var ss_size: CGSize {
|
||||
get {
|
||||
frame.size
|
||||
}
|
||||
set {
|
||||
var frame = frame
|
||||
frame.size = newValue
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
var ss_origin: CGPoint {
|
||||
get {
|
||||
frame.origin
|
||||
}
|
||||
set {
|
||||
var frame = frame
|
||||
frame.origin = newValue
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
var ss_center: CGPoint {
|
||||
get {
|
||||
center
|
||||
}
|
||||
set {
|
||||
center = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var ss_x: CGFloat {
|
||||
get {
|
||||
frame.origin.x
|
||||
}
|
||||
set {
|
||||
var frame = frame
|
||||
frame.origin.x = newValue
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
var ss_maxX: CGFloat {
|
||||
get {
|
||||
frame.maxX
|
||||
}
|
||||
}
|
||||
|
||||
var ss_y: CGFloat {
|
||||
get {
|
||||
frame.origin.y
|
||||
}
|
||||
set {
|
||||
var frame = frame
|
||||
frame.origin.y = newValue
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
var ss_maxY: CGFloat {
|
||||
get {
|
||||
frame.maxY
|
||||
}
|
||||
set {
|
||||
var frame = frame
|
||||
frame.origin.y = newValue
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
var ss_w: CGFloat {
|
||||
get {
|
||||
frame.width
|
||||
}
|
||||
set {
|
||||
var frame = frame
|
||||
frame.size.width = newValue
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
var ss_h: CGFloat {
|
||||
get {
|
||||
frame.height
|
||||
}
|
||||
set {
|
||||
var frame = frame
|
||||
frame.size.height = newValue
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
var ss_centerX: CGFloat {
|
||||
get {
|
||||
center.x
|
||||
}
|
||||
set {
|
||||
var center = center
|
||||
center.x = newValue
|
||||
self.center = center
|
||||
}
|
||||
}
|
||||
|
||||
var ss_centerY: CGFloat {
|
||||
get {
|
||||
center.y
|
||||
}
|
||||
set {
|
||||
var center = center
|
||||
center.y = newValue
|
||||
self.center = center
|
||||
}
|
||||
}
|
||||
|
||||
func ss_moveToCenter() {
|
||||
if let superV = superview {
|
||||
ss_center = CGPoint(x: superV.frame.width / 2, y: superV.frame.height / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user