Skip to content

Commit ae7d68f

Browse files
committed
refactor: add PushBackAnimationGroup
1 parent 4d2e3fc commit ae7d68f

8 files changed

Lines changed: 98 additions & 102 deletions

Demo.xcodeproj/project.pbxproj

Lines changed: 17 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
344F2B7E1ED95F17B47EC678 /* Pods_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8742E3B9E9E66188D37018C /* Pods_Demo.framework */; };
11-
8D4B7D431D853DE80013459B /* ClosureWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D3D1D853DE80013459B /* ClosureWrapper.swift */; };
12-
8D4B7D441D853DE80013459B /* UIDevice+SemiModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D3E1D853DE80013459B /* UIDevice+SemiModalViewController.swift */; };
13-
8D4B7D451D853DE80013459B /* UIView+FindViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D3F1D853DE80013459B /* UIView+FindViewController.swift */; };
14-
8D4B7D461D853DE80013459B /* UIView+SemiModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D401D853DE80013459B /* UIView+SemiModalViewController.swift */; };
15-
8D4B7D471D853DE80013459B /* UIViewController+Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D411D853DE80013459B /* UIViewController+Options.swift */; };
16-
8D4B7D481D853DE80013459B /* UIViewController+SemiModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D421D853DE80013459B /* UIViewController+SemiModalViewController.swift */; };
10+
08B08C2B13A15184A3E63F5D /* Pods_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2795497DC42E0EA345871CD8 /* Pods_Demo.framework */; };
1711
8D4B7D531D853F1B0013459B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B7D4A1D853F1B0013459B /* AppDelegate.swift */; };
1812
8D4B7D541D853F1B0013459B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D4B7D4B1D853F1B0013459B /* Assets.xcassets */; };
1913
8D4B7D551D853F1B0013459B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D4B7D4C1D853F1B0013459B /* LaunchScreen.storyboard */; };
@@ -24,14 +18,9 @@
2418
/* End PBXBuildFile section */
2519

2620
/* Begin PBXFileReference section */
21+
2795497DC42E0EA345871CD8 /* Pods_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2722
45072DEAF5EB3BCBC2B79648 /* Pods-SemiModalViewController.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SemiModalViewController.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SemiModalViewController/Pods-SemiModalViewController.debug.xcconfig"; sourceTree = "<group>"; };
2823
6EA5AA50658935AF6140E12F /* Pods-SemiModalViewController.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SemiModalViewController.release.xcconfig"; path = "Pods/Target Support Files/Pods-SemiModalViewController/Pods-SemiModalViewController.release.xcconfig"; sourceTree = "<group>"; };
29-
8D4B7D3D1D853DE80013459B /* ClosureWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClosureWrapper.swift; sourceTree = "<group>"; };
30-
8D4B7D3E1D853DE80013459B /* UIDevice+SemiModalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDevice+SemiModalViewController.swift"; sourceTree = "<group>"; };
31-
8D4B7D3F1D853DE80013459B /* UIView+FindViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+FindViewController.swift"; sourceTree = "<group>"; };
32-
8D4B7D401D853DE80013459B /* UIView+SemiModalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+SemiModalViewController.swift"; sourceTree = "<group>"; };
33-
8D4B7D411D853DE80013459B /* UIViewController+Options.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Options.swift"; sourceTree = "<group>"; };
34-
8D4B7D421D853DE80013459B /* UIViewController+SemiModalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+SemiModalViewController.swift"; sourceTree = "<group>"; };
3524
8D4B7D4A1D853F1B0013459B /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
3625
8D4B7D4B1D853F1B0013459B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
3726
8D4B7D4D1D853F1B0013459B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
@@ -42,7 +31,6 @@
4231
8D642E6E1D79758E00C45795 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
4332
8D76E7142150D0320019CC99 /* SemiViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemiViewController.swift; sourceTree = "<group>"; };
4433
BF8C779DD9D1680FE694014C /* Pods-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.release.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig"; sourceTree = "<group>"; };
45-
D8742E3B9E9E66188D37018C /* Pods_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4634
EC450E88D8C6359EF36F028C /* Pods-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig"; sourceTree = "<group>"; };
4735
/* End PBXFileReference section */
4836

@@ -51,26 +39,13 @@
5139
isa = PBXFrameworksBuildPhase;
5240
buildActionMask = 2147483647;
5341
files = (
54-
344F2B7E1ED95F17B47EC678 /* Pods_Demo.framework in Frameworks */,
42+
08B08C2B13A15184A3E63F5D /* Pods_Demo.framework in Frameworks */,
5543
);
5644
runOnlyForDeploymentPostprocessing = 0;
5745
};
5846
/* End PBXFrameworksBuildPhase section */
5947

6048
/* Begin PBXGroup section */
61-
8D4B7D3C1D853DE80013459B /* Source */ = {
62-
isa = PBXGroup;
63-
children = (
64-
8D4B7D3D1D853DE80013459B /* ClosureWrapper.swift */,
65-
8D4B7D3E1D853DE80013459B /* UIDevice+SemiModalViewController.swift */,
66-
8D4B7D3F1D853DE80013459B /* UIView+FindViewController.swift */,
67-
8D4B7D401D853DE80013459B /* UIView+SemiModalViewController.swift */,
68-
8D4B7D411D853DE80013459B /* UIViewController+Options.swift */,
69-
8D4B7D421D853DE80013459B /* UIViewController+SemiModalViewController.swift */,
70-
);
71-
path = Source;
72-
sourceTree = "<group>";
73-
};
7449
8D4B7D491D853F1B0013459B /* Demo */ = {
7550
isa = PBXGroup;
7651
children = (
@@ -90,7 +65,6 @@
9065
isa = PBXGroup;
9166
children = (
9267
8D4B7D491D853F1B0013459B /* Demo */,
93-
8D4B7D3C1D853DE80013459B /* Source */,
9468
8D642E6F1D79758E00C45795 /* Products */,
9569
EC8873D9C3439B3D5EE58432 /* Pods */,
9670
E15FBAFD47163413F3D1A7EC /* Frameworks */,
@@ -108,7 +82,7 @@
10882
E15FBAFD47163413F3D1A7EC /* Frameworks */ = {
10983
isa = PBXGroup;
11084
children = (
111-
D8742E3B9E9E66188D37018C /* Pods_Demo.framework */,
85+
2795497DC42E0EA345871CD8 /* Pods_Demo.framework */,
11286
);
11387
name = Frameworks;
11488
sourceTree = "<group>";
@@ -135,7 +109,7 @@
135109
8D642E6A1D79758E00C45795 /* Sources */,
136110
8D642E6B1D79758E00C45795 /* Frameworks */,
137111
8D642E6C1D79758E00C45795 /* Resources */,
138-
313330B1C9A5407ABD430F40 /* [CP] Embed Pods Frameworks */,
112+
A39697C889D68159C745087A /* [CP] Embed Pods Frameworks */,
139113
);
140114
buildRules = (
141115
);
@@ -194,40 +168,40 @@
194168
/* End PBXResourcesBuildPhase section */
195169

196170
/* Begin PBXShellScriptBuildPhase section */
197-
313330B1C9A5407ABD430F40 /* [CP] Embed Pods Frameworks */ = {
171+
92C885EEE249003944DFA33D /* [CP] Check Pods Manifest.lock */ = {
198172
isa = PBXShellScriptBuildPhase;
199173
buildActionMask = 2147483647;
200174
files = (
201175
);
202176
inputPaths = (
203-
"${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh",
204-
"${BUILT_PRODUCTS_DIR}/SemiModalViewController/SemiModalViewController.framework",
177+
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
178+
"${PODS_ROOT}/Manifest.lock",
205179
);
206-
name = "[CP] Embed Pods Frameworks";
180+
name = "[CP] Check Pods Manifest.lock";
207181
outputPaths = (
208-
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SemiModalViewController.framework",
182+
"$(DERIVED_FILE_DIR)/Pods-Demo-checkManifestLockResult.txt",
209183
);
210184
runOnlyForDeploymentPostprocessing = 0;
211185
shellPath = /bin/sh;
212-
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh\"\n";
186+
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
213187
showEnvVarsInLog = 0;
214188
};
215-
92C885EEE249003944DFA33D /* [CP] Check Pods Manifest.lock */ = {
189+
A39697C889D68159C745087A /* [CP] Embed Pods Frameworks */ = {
216190
isa = PBXShellScriptBuildPhase;
217191
buildActionMask = 2147483647;
218192
files = (
219193
);
220194
inputPaths = (
221-
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
222-
"${PODS_ROOT}/Manifest.lock",
195+
"${SRCROOT}/Pods/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh",
196+
"${BUILT_PRODUCTS_DIR}/SemiModalViewController/SemiModalViewController.framework",
223197
);
224-
name = "[CP] Check Pods Manifest.lock";
198+
name = "[CP] Embed Pods Frameworks";
225199
outputPaths = (
226-
"$(DERIVED_FILE_DIR)/Pods-Demo-checkManifestLockResult.txt",
200+
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SemiModalViewController.framework",
227201
);
228202
runOnlyForDeploymentPostprocessing = 0;
229203
shellPath = /bin/sh;
230-
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
204+
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh\"\n";
231205
showEnvVarsInLog = 0;
232206
};
233207
/* End PBXShellScriptBuildPhase section */
@@ -238,15 +212,9 @@
238212
buildActionMask = 2147483647;
239213
files = (
240214
8D76E7152150D0320019CC99 /* SemiViewController.swift in Sources */,
241-
8D4B7D461D853DE80013459B /* UIView+SemiModalViewController.swift in Sources */,
242-
8D4B7D441D853DE80013459B /* UIDevice+SemiModalViewController.swift in Sources */,
243-
8D4B7D431D853DE80013459B /* ClosureWrapper.swift in Sources */,
244215
8D4B7D591D853F1B0013459B /* SecondViewController.swift in Sources */,
245216
8D4B7D571D853F1B0013459B /* FirstViewController.swift in Sources */,
246-
8D4B7D471D853DE80013459B /* UIViewController+Options.swift in Sources */,
247217
8D4B7D531D853F1B0013459B /* AppDelegate.swift in Sources */,
248-
8D4B7D481D853DE80013459B /* UIViewController+SemiModalViewController.swift in Sources */,
249-
8D4B7D451D853DE80013459B /* UIView+FindViewController.swift in Sources */,
250218
);
251219
runOnlyForDeploymentPostprocessing = 0;
252220
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>

Demo/FirstViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import UIKit
2+
import SemiModalViewController
23

34
class FirstViewController: UIViewController {
45

@@ -9,7 +10,7 @@ class FirstViewController: UIViewController {
910

1011
@IBAction func show(_ sender: AnyObject) {
1112
let view = UIView(frame: UIScreen.main.bounds)
12-
view.height = 300
13+
view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 300)
1314
view.backgroundColor = UIColor.red
1415

1516
let options: [SemiModalOption : Any] = [

Demo/SecondViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import UIKit
2+
import SemiModalViewController
23

34
class SecondViewController: UIViewController {
45

@@ -16,7 +17,7 @@ class SecondViewController: UIViewController {
1617
let identifier = String(describing: SemiViewController.self)
1718
let controller = storyboard.instantiateViewController(withIdentifier: identifier)
1819

19-
controller.view.height = 200
20+
controller.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 200)
2021
controller.view.backgroundColor = UIColor.red
2122

2223
presentSemiViewController(controller, options: options, completion: {

Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PODS:
2-
- SemiModalViewController (0.3)
2+
- SemiModalViewController (0.4)
33

44
DEPENDENCIES:
55
- SemiModalViewController (from `./`)
@@ -9,7 +9,7 @@ EXTERNAL SOURCES:
99
:path: "./"
1010

1111
SPEC CHECKSUMS:
12-
SemiModalViewController: dc17361205906ae46cb45d23543c594b0589a254
12+
SemiModalViewController: 8f0e031f3cc58057725ee65ed9f58aada09290ed
1313

1414
PODFILE CHECKSUM: 7a75cd15e7190183aa62892cb568390740ff2e74
1515

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import Foundation
2+
import UIKit
3+
4+
public class PushBackAnimationGroup: CAAnimationGroup {
5+
public convenience init(forward: Bool, viewHeight: CGFloat, options: [SemiModalOption: Any]) {
6+
self.init()
7+
8+
var id1 = CATransform3DIdentity
9+
id1.m34 = 1.0 / -900
10+
id1 = CATransform3DScale(id1, 0.95, 0.95, 1)
11+
12+
let angleFactor: CGFloat = UIDevice.isPad() ? 7.5 : 15.0
13+
id1 = CATransform3DRotate(id1, angleFactor * CGFloat(Double.pi) / 180.0, 1, 0, 0)
14+
15+
var id2 = CATransform3DIdentity
16+
id2.m34 = id1.m34
17+
18+
let scale = CGFloat(options[.parentScale] as! Double)
19+
let tzFactor: CGFloat = UIDevice.isPad() ? -0.04 : -0.08
20+
21+
id2 = CATransform3DTranslate(id2, 0, viewHeight * tzFactor, 0)
22+
id2 = CATransform3DScale(id2, scale, scale, 1)
23+
24+
let animation = CABasicAnimation(keyPath: "transform")
25+
animation.toValue = NSValue(caTransform3D: id1)
26+
27+
let animationDuration = options[.animationDuration] as! Double
28+
animation.duration = animationDuration / 2
29+
animation.fillMode = CAMediaTimingFillMode.forwards
30+
animation.isRemovedOnCompletion = false
31+
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
32+
33+
let animation2 = CABasicAnimation(keyPath: "transform")
34+
animation2.toValue = NSValue(caTransform3D: forward ? id2 : CATransform3DIdentity)
35+
animation2.beginTime = animation.duration
36+
animation2.duration = animation.duration
37+
animation2.fillMode = CAMediaTimingFillMode.forwards
38+
animation2.isRemovedOnCompletion = false
39+
40+
fillMode = CAMediaTimingFillMode.forwards
41+
isRemovedOnCompletion = false
42+
duration = animation.duration * 2
43+
animations = [animation, animation2]
44+
}
45+
}

Source/UIViewController+Options.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,28 @@ extension UIViewController {
2424
targetVC = targetVC.parent!
2525
}
2626

27-
objc_setAssociatedObject(targetVC, &CustomOptions, options, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
27+
objc_setAssociatedObject(targetVC, &CustomOptions, options, .OBJC_ASSOCIATION_RETAIN)
2828
}
2929

30-
func optionForKey(_ optionKey: SemiModalOption) -> Any? {
30+
func options() -> [SemiModalOption: Any] {
3131
var targetVC: UIViewController = self
3232
while targetVC.parent != nil {
3333
targetVC = targetVC.parent!
3434
}
3535

36-
guard let options = objc_getAssociatedObject(targetVC, &CustomOptions) as? [SemiModalOption: Any]
37-
, let value = options[optionKey] else {
38-
return defaultOptions[optionKey]
36+
if let options = objc_getAssociatedObject(targetVC, &CustomOptions) as? [SemiModalOption: Any] {
37+
var defaultOptions: [SemiModalOption: Any] = self.defaultOptions
38+
defaultOptions.merge(options) { (_, new) in new }
39+
40+
return defaultOptions
41+
} else {
42+
return defaultOptions
3943
}
44+
}
45+
46+
func optionForKey(_ optionKey: SemiModalOption) -> Any? {
47+
let options = self.options()
48+
let value = options[optionKey]
4049

4150
switch optionKey {
4251
case .traverseParentHierarchy, .pushParentBack, .disableCancel:

0 commit comments

Comments
 (0)