-
Notifications
You must be signed in to change notification settings - Fork 748
/
Copy pathPhysicsAnimation.swift
125 lines (107 loc) · 4.86 KB
/
PhysicsAnimation.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//
// PhysicsAnimation.swift
// SwiftMessages
//
// Created by Timothy Moose on 6/14/17.
// Copyright © 2017 SwiftKick Mobile. All rights reserved.
//
import UIKit
public class PhysicsAnimation: NSObject, Animator {
public enum Placement {
case top
case center
case bottom
}
open var placement: Placement = .center
open var showDuration: TimeInterval = 0.5
open var hideDuration: TimeInterval = 0.15
public var panHandler = PhysicsPanHandler()
public weak var delegate: AnimationDelegate?
weak var messageView: UIView?
weak var containerView: UIView?
var context: AnimationContext?
public override init() {}
init(delegate: AnimationDelegate) {
self.delegate = delegate
}
public func show(context: AnimationContext, completion: @escaping AnimationCompletion) {
NotificationCenter.default.addObserver(self, selector: #selector(adjustMargins), name: UIDevice.orientationDidChangeNotification, object: nil)
install(context: context)
showAnimation(context: context, completion: completion)
}
public func hide(context: AnimationContext, completion: @escaping AnimationCompletion) {
NotificationCenter.default.removeObserver(self)
if panHandler.isOffScreen {
context.messageView.alpha = 0
panHandler.state?.stop()
}
let view = context.messageView
self.context = context
CATransaction.begin()
CATransaction.setCompletionBlock {
view.alpha = 1
view.transform = CGAffineTransform.identity
completion(true)
}
UIView.animate(withDuration: hideDuration, delay: 0, options: [.beginFromCurrentState, .curveEaseIn, .allowUserInteraction], animations: {
view.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
}, completion: nil)
UIView.animate(withDuration: hideDuration, delay: 0, options: [.beginFromCurrentState, .curveEaseIn, .allowUserInteraction], animations: {
view.alpha = 0
}, completion: nil)
CATransaction.commit()
}
func install(context: AnimationContext) {
let view = context.messageView
let container = context.containerView
messageView = view
containerView = container
self.context = context
view.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(view)
switch placement {
case .center:
view.centerYAnchor.constraint(equalTo: container.centerYAnchor).with(priority: UILayoutPriority(200)).isActive = true
case .top:
view.topAnchor.constraint(equalTo: container.topAnchor).with(priority: UILayoutPriority(200)).isActive = true
case .bottom:
view.bottomAnchor.constraint(equalTo: container.bottomAnchor).with(priority: UILayoutPriority(200)).isActive = true
}
NSLayoutConstraint(item: view, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: view, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
// Important to layout now in order to get the right safe area insets
container.layoutIfNeeded()
adjustMargins()
container.layoutIfNeeded()
installInteractive(context: context)
}
@objc public func adjustMargins() {
guard let adjustable = messageView as? MarginAdjustable & UIView,
let context = context else { return }
adjustable.preservesSuperviewLayoutMargins = false
if #available(iOS 11, *) {
adjustable.insetsLayoutMarginsFromSafeArea = false
}
adjustable.layoutMargins = adjustable.defaultMarginAdjustment(context: context)
}
func showAnimation(context: AnimationContext, completion: @escaping AnimationCompletion) {
let view = context.messageView
view.alpha = 0.25
view.transform = CGAffineTransform(scaleX: 0.6, y: 0.6)
CATransaction.begin()
CATransaction.setCompletionBlock {
completion(true)
}
UIView.animate(withDuration: showDuration, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0, options: [.beginFromCurrentState, .curveLinear, .allowUserInteraction], animations: {
view.transform = CGAffineTransform.identity
}, completion: nil)
UIView.animate(withDuration: 0.3 * showDuration, delay: 0, options: [.beginFromCurrentState, .curveLinear, .allowUserInteraction], animations: {
view.alpha = 1
}, completion: nil)
CATransaction.commit()
}
func installInteractive(context: AnimationContext) {
guard context.interactiveHide else { return }
panHandler.configure(context: context, animator: self)
}
}