Skip to content
This repository was archived by the owner on Aug 13, 2021. It is now read-only.

Commit 47d2f30

Browse files
author
Jeff Verkoeyen
committed
Merge branch 'release-candidate' into stable
2 parents a76ad74 + a02f525 commit 47d2f30

File tree

102 files changed

+2004
-1011
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+2004
-1011
lines changed

.jazzy.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
module: MaterialMotion
2-
module_version: 1.3.0
2+
module_version: 2.0.0
33
sdk: iphonesimulator
44
xcodebuild_arguments:
55
- -workspace
66
- MaterialMotion.xcworkspace
77
- -scheme
88
- MaterialMotion
99
github_url: https://github.com/material-motion/material-motion-swift
10-
github_file_prefix: https://github.com/material-motion/material-motion-swift/tree/v1.3.0
10+
github_file_prefix: https://github.com/material-motion/material-motion-swift/tree/v2.0.0

CHANGELOG.md

Lines changed: 328 additions & 0 deletions
Large diffs are not rendered by default.

MaterialMotion.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "MaterialMotion"
33
s.summary = "Reactive motion driven by Core Animation."
4-
s.version = "1.3.0"
4+
s.version = "2.0.0"
55
s.authors = "The Material Motion Authors"
66
s.license = "Apache 2.0"
77
s.homepage = "https://github.com/material-motion/material-motion-swift"
@@ -11,5 +11,5 @@ Pod::Spec.new do |s|
1111

1212
s.source_files = "src/**/*.{swift}"
1313

14-
s.dependency "IndefiniteObservable", "~> 3.0"
14+
s.dependency "IndefiniteObservable", "~> 4.0"
1515
end

Podfile.lock

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
PODS:
2-
- CatalogByConvention (2.0.0)
3-
- IndefiniteObservable (3.1.0):
4-
- IndefiniteObservable/lib (= 3.1.0)
5-
- IndefiniteObservable/lib (3.1.0)
6-
- MaterialMotion (1.3.0):
7-
- IndefiniteObservable (~> 3.0)
2+
- CatalogByConvention (2.1.0)
3+
- IndefiniteObservable (4.0.0):
4+
- IndefiniteObservable/lib (= 4.0.0)
5+
- IndefiniteObservable/lib (4.0.0)
6+
- MaterialMotion (2.0.0):
7+
- IndefiniteObservable (~> 4.0)
88

99
DEPENDENCIES:
1010
- CatalogByConvention
@@ -15,10 +15,10 @@ EXTERNAL SOURCES:
1515
:path: "./"
1616

1717
SPEC CHECKSUMS:
18-
CatalogByConvention: be55c2263132e4f9f59299ac8a528ee8715b3275
19-
IndefiniteObservable: 2789d61f487d8d37fa2b9c3153cc44d4447ff744
20-
MaterialMotion: b5040104b109cf9680a2c4a77296c429dab2c376
18+
CatalogByConvention: ef0913973b86b4234bcadf22aa84037c4a47cbbd
19+
IndefiniteObservable: 1ee6af37efa8763a222cc6d414cd125e26ed9b6a
20+
MaterialMotion: a412b109cfe4ab7b1fd956409a5da9a3635d3dce
2121

2222
PODFILE CHECKSUM: f503265a0d60526a0d28c96dd4bdcfb40fb562fc
2323

24-
COCOAPODS: 1.2.0
24+
COCOAPODS: 1.2.1

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/MaterialMotion.svg)](https://cocoapods.org/pods/MaterialMotion)
1010
[![Platform](https://img.shields.io/cocoapods/p/MaterialMotion.svg)](http://cocoadocs.org/docsets/MaterialMotion)
1111
[![Docs](https://img.shields.io/cocoapods/metrics/doc-percent/MaterialMotion.svg)](http://cocoadocs.org/docsets/MaterialMotion)
12+
[![Chat](https://img.shields.io/discord/198544450366996480.svg)](https://discord.gg/material-motion)
1213

1314
This library includes a variety of ready-to-use **interactions**. Interactions are registered to an
1415
instance of `MotionRuntime`:

examples/CarouselExample.swift

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import MaterialMotion
1919

2020
class CarouselExampleViewController: ExampleViewController, UIScrollViewDelegate {
2121

22-
var runtime: MotionRuntime!
22+
let delegate = ReactiveScrollViewDelegate()
2323
override func viewDidLoad() {
2424
super.viewDidLoad()
2525

@@ -29,10 +29,10 @@ class CarouselExampleViewController: ExampleViewController, UIScrollViewDelegate
2929
scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
3030
scrollView.isPagingEnabled = true
3131
scrollView.contentSize = .init(width: view.bounds.size.width * 3, height: view.bounds.size.height)
32-
scrollView.delegate = self
32+
scrollView.delegate = delegate
3333
view.addSubview(scrollView)
3434

35-
pager = UIPageControl()
35+
let pager = UIPageControl()
3636
let size = pager.sizeThatFits(view.bounds.size)
3737
pager.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
3838
pager.frame = .init(x: 0, y: view.bounds.height - size.height - 20, width: view.bounds.width, height: size.height)
@@ -45,9 +45,6 @@ class CarouselExampleViewController: ExampleViewController, UIScrollViewDelegate
4545
(title: "Page 3", description: "Page 3 description", color: .secondaryColor),
4646
]
4747

48-
runtime = MotionRuntime(containerView: view)
49-
50-
let stream = runtime.get(scrollView)
5148
for (index, data) in datas.enumerated() {
5249
let page = CarouselPage(frame: view.bounds)
5350
page.frame.origin.x = CGFloat(index) * view.bounds.width
@@ -56,21 +53,19 @@ class CarouselExampleViewController: ExampleViewController, UIScrollViewDelegate
5653
page.iconView.backgroundColor = data.color
5754
scrollView.addSubview(page)
5855

59-
let pageEdge = stream.x().offset(by: -page.frame.origin.x)
56+
let pageEdge = delegate.x().offset(by: -page.frame.origin.x)
6057

61-
runtime.connect(pageEdge.rewriteRange(start: 0, end: 128,
62-
destinationStart: 1, destinationEnd: 0),
63-
to: runtime.get(page).alpha)
64-
runtime.connect(pageEdge.rewriteRange(start: -view.bounds.width, end: 0,
65-
destinationStart: 0.5, destinationEnd: 1.0),
66-
to: runtime.get(page.layer).scale)
58+
pageEdge.rewriteRange(start: 0, end: 128, destinationStart: 1, destinationEnd: 0).subscribeToValue {
59+
page.alpha = $0
60+
}
61+
pageEdge.rewriteRange(start: -view.bounds.width, end: 0, destinationStart: 0.5, destinationEnd: 1.0).subscribeToValue {
62+
page.layer.transform = CATransform3DMakeScale($0, $0, 1)
63+
}
6764
}
68-
}
6965

70-
var pager: UIPageControl!
71-
72-
func scrollViewDidScroll(_ scrollView: UIScrollView) {
73-
pager.currentPage = Int((scrollView.contentOffset.x + scrollView.bounds.width / 2) / scrollView.bounds.width)
66+
delegate.x().offset(by: scrollView.bounds.width / 2).scaled(by: 1 / scrollView.bounds.width).subscribeToValue {
67+
pager.currentPage = Int($0)
68+
}
7469
}
7570

7671
override func exampleInformation() -> ExampleInfo {

examples/ContextualTransitionExample.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class PhotoAlbumViewController: UIViewController, UICollectionViewDataSource, UI
165165

166166
super.init(nibName: nil, bundle: nil)
167167

168-
transitionController.transitionType = PushBackTransition.self
168+
transitionController.transition = PushBackTransition()
169169
}
170170

171171
required init?(coder aDecoder: NSCoder) {
@@ -202,11 +202,10 @@ class PhotoAlbumViewController: UIViewController, UICollectionViewDataSource, UI
202202

203203
view.addSubview(collectionView)
204204

205-
let dismisser = transitionController.dismisser
206-
dismisser.disableSimultaneousRecognition(of: collectionView.panGestureRecognizer)
205+
transitionController.disableSimultaneousRecognition(of: collectionView.panGestureRecognizer)
207206

208207
for gesture in [UIPanGestureRecognizer(), UIPinchGestureRecognizer(), UIRotationGestureRecognizer()] {
209-
dismisser.dismissWhenGestureRecognizerBegins(gesture)
208+
transitionController.dismissWhenGestureRecognizerBegins(gesture)
210209
view.addGestureRecognizer(gesture)
211210
}
212211
}
@@ -227,6 +226,10 @@ class PhotoAlbumViewController: UIViewController, UICollectionViewDataSource, UI
227226
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
228227
}
229228

229+
override var preferredStatusBarStyle: UIStatusBarStyle {
230+
return .lightContent
231+
}
232+
230233
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
231234
return album.photos.count
232235
}
@@ -255,8 +258,6 @@ class PhotoAlbumViewController: UIViewController, UICollectionViewDataSource, UI
255258

256259
private class PushBackTransition: Transition {
257260

258-
required init() {}
259-
260261
func willBeginTransition(withContext ctx: TransitionContext, runtime: MotionRuntime) -> [Stateful] {
261262
let foreVC = ctx.fore as! PhotoAlbumViewController
262263
let foreImageView = (foreVC.collectionView.cellForItem(at: foreVC.indexPathForCurrentPhoto()) as! PhotoCollectionViewCell).imageView
@@ -282,7 +283,7 @@ private class PushBackTransition: Transition {
282283

283284
let size = TransitionSpring(back: contextView.bounds.size, fore: fitSize, direction: ctx.direction)
284285
runtime.toggle(size, inReactionTo: draggable)
285-
runtime.add(size, to: runtime.get(replicaView).reactiveLayer.size)
286+
runtime.add(size, to: runtime.get(replicaView).layer.size)
286287

287288
let opacity = TransitionSpring<CGFloat>(back: 0, fore: 1, direction: ctx.direction)
288289
runtime.add(opacity, to: runtime.get(ctx.fore.view.layer).opacity)

examples/FabTransitionExample.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class FabTransitionExampleViewController: ExampleViewController, TransitionConte
4141

4242
func didTap() {
4343
let vc = ModalViewController()
44-
vc.transitionController.transitionType = CircularRevealTransition.self
44+
vc.transitionController.transition = CircularRevealTransition()
4545
present(vc, animated: true)
4646
}
4747

@@ -76,18 +76,17 @@ private class ModalViewController: UIViewController {
7676

7777
let floodFillOvershootRatio: CGFloat = 1.2
7878

79-
private class CircularRevealTransition: Transition {
79+
private class CircularRevealTransition: TransitionWithTermination {
8080

8181
// TODO: Support for transient views.
8282
var floodFillView: UIView!
8383
var foreViewLayer: CALayer!
84-
deinit {
84+
85+
func didEndTransition(withContext ctx: TransitionContext, runtime: MotionRuntime) {
8586
floodFillView.removeFromSuperview()
8687
foreViewLayer.mask = nil
8788
}
8889

89-
required init() {}
90-
9190
func willBeginTransition(withContext ctx: TransitionContext, runtime: MotionRuntime) -> [Stateful] {
9291
foreViewLayer = ctx.fore.view.layer
9392

@@ -124,14 +123,14 @@ private class CircularRevealTransition: Transition {
124123
let foreShadowPath = CGRect(origin: .zero(), size: expandedSize)
125124
let shadowPath = tween(back: floodFillView.layer.shadowPath!, fore: UIBezierPath(ovalIn: foreShadowPath).cgPath, ctx: ctx)
126125

127-
let floodLayer = runtime.get(floodFillView).reactiveLayer
126+
let floodLayer = runtime.get(floodFillView).layer
128127
runtime.add(expansion, to: floodLayer.size)
129128
runtime.add(fadeOut, to: floodLayer.opacity)
130129
runtime.add(radius, to: floodLayer.cornerRadius)
131130
runtime.add(shadowPath, to: floodLayer.shadowPath)
132131

133132
let shiftIn = tween(back: ctx.fore.view.layer.position.y + 40, fore: ctx.fore.view.layer.position.y, ctx: ctx)
134-
runtime.add(shiftIn, to: runtime.get(ctx.fore.view).reactiveLayer.positionY)
133+
runtime.add(shiftIn, to: runtime.get(ctx.fore.view).layer.positionY)
135134

136135
let maskShiftIn = tween(back: CGFloat(-40), fore: CGFloat(0), ctx: ctx)
137136
runtime.add(maskShiftIn, to: runtime.get(maskLayer).positionY)

examples/HowToMakeACustomOperatorExample.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ class HowToMakeACustomOperatorExampleViewController: ExampleViewController {
4040
extension MotionObservableConvertible where T == CGPoint {
4141

4242
fileprivate func wobble(width: CGFloat) -> MotionObservable<CGPoint> {
43-
return _map(#function) {
44-
.init(x: $0.x + sin($0.y / 50) * width, y: $0.y)
45-
}
43+
return _map { .init(x: $0.x + sin($0.y / 50) * width, y: $0.y) }
4644
}
4745
}

examples/InteractivePushBackTransitionExample.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private class ModalViewController: UIViewController, UIGestureRecognizerDelegate
4141
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
4242
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
4343

44-
transitionController.transitionType = PushBackTransition.self
44+
transitionController.transition = PushBackTransition()
4545
}
4646

4747
required init?(coder aDecoder: NSCoder) {
@@ -59,17 +59,19 @@ private class ModalViewController: UIViewController, UIGestureRecognizerDelegate
5959
view.addSubview(scrollView)
6060

6161
let pan = UIPanGestureRecognizer()
62-
pan.delegate = transitionController.dismisser.topEdgeDismisserDelegate(for: scrollView)
63-
transitionController.dismisser.dismissWhenGestureRecognizerBegins(pan)
62+
pan.delegate = transitionController.topEdgeDismisserDelegate(for: scrollView)
63+
transitionController.dismissWhenGestureRecognizerBegins(pan)
6464
scrollView.panGestureRecognizer.require(toFail: pan)
6565
view.addGestureRecognizer(pan)
6666
}
67+
68+
override var preferredStatusBarStyle: UIStatusBarStyle {
69+
return .lightContent
70+
}
6771
}
6872

6973
private class PushBackTransition: Transition {
7074

71-
required init() {}
72-
7375
func willBeginTransition(withContext ctx: TransitionContext, runtime: MotionRuntime) -> [Stateful] {
7476
let draggable = Draggable(withFirstGestureIn: ctx.gestureRecognizers)
7577

examples/MaterialExpansionExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class MaterialExpansionExampleViewController: ExampleViewController {
6161
let floodExpansion = Tween<CGFloat>(duration: 0.375, values: [0, 1])
6262
floodExpansion.timingFunctions.value = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
6363
let fadeOut = Tween<CGFloat>(duration: 0.375, values: [0.75, 0])
64-
fadeOut.keyPositions.value = [0.2, 1]
64+
fadeOut.offsets.value = [0.2, 1]
6565

6666
runtime.add(SetPositionOnTap(.withExistingRecognizer(tap.gestureRecognizer)),
6767
to: runtime.get(flood.layer).position)

examples/ModalDialogExample.swift

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ class ModalDialogViewController: UIViewController {
4545
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
4646
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
4747

48-
transitionController.transitionType = ModalDialogTransition.self
49-
50-
modalPresentationStyle = .overCurrentContext
48+
transitionController.transition = ModalDialogTransition()
5149
}
5250

5351
required init?(coder aDecoder: NSCoder) {
@@ -64,44 +62,48 @@ class ModalDialogViewController: UIViewController {
6462
view.layer.shadowRadius = 5
6563
view.layer.shadowOpacity = 1
6664
view.layer.shadowOffset = .init(width: 0, height: 2)
67-
68-
preferredContentSize = .init(width: 200, height: 200)
6965
}
7066
}
7167

72-
class ModalDialogTransition: SelfDismissingTransition {
68+
class ModalDialogTransition: SelfDismissingTransition, TransitionWithPresentation {
7369

74-
required init() {}
70+
public func defaultModalPresentationStyle() -> UIModalPresentationStyle? {
71+
return .custom
72+
}
7573

76-
func willBeginTransition(withContext ctx: TransitionContext, runtime: MotionRuntime) -> [Stateful] {
77-
let size = ctx.fore.preferredContentSize
74+
public func presentationController(forPresented presented: UIViewController,
75+
presenting: UIViewController?,
76+
source: UIViewController) -> UIPresentationController? {
77+
return ModalDialogPresentationController(presentedViewController: presented,
78+
presenting: presenting)
79+
}
7880

79-
if ctx.direction == .forward {
80-
ctx.fore.view.bounds = CGRect(origin: .zero, size: size)
81-
}
8281

82+
func willBeginTransition(withContext ctx: TransitionContext, runtime: MotionRuntime) -> [Stateful] {
83+
let size = ctx.fore.view.frame.size
8384
let bounds = ctx.containerView().bounds
8485
let backPosition = CGPoint(x: bounds.midX, y: bounds.maxY + size.height * 3 / 4)
85-
let forePosition = CGPoint(x: bounds.midX, y: bounds.midY)
86+
let forePosition = ctx.fore.view.layer.position
8687

8788
let reactiveForeLayer = runtime.get(ctx.fore.view.layer)
8889
let position = reactiveForeLayer.position
8990

9091
let draggable = Draggable(withFirstGestureIn: ctx.gestureRecognizers)
9192

92-
let gesture = runtime.get(draggable.nextGestureRecognizer)
9393
let centerY = ctx.containerView().bounds.height / 2.0
9494

9595
runtime.add(ChangeDirection(withVelocityOf: draggable.nextGestureRecognizer, whenNegative: .forward),
9696
to: ctx.direction)
9797

98-
runtime.connect(gesture
99-
.velocityOnReleaseStream()
100-
.y()
101-
.thresholdRange(min: -100, max: 100)
102-
.rewrite([.within: position.y().threshold(centerY).rewrite([.below: .forward,
103-
.above: .backward])]),
104-
to: ctx.direction)
98+
if let gesture = draggable.nextGestureRecognizer {
99+
runtime.connect(runtime.get(gesture)
100+
.velocityOnReleaseStream()
101+
.y()
102+
.thresholdRange(min: -100, max: 100)
103+
.rewrite([.within: position.y().threshold(centerY).rewrite([.below: .forward,
104+
.above: .backward])]),
105+
to: ctx.direction)
106+
}
105107

106108
let movement = TransitionSpring(back: backPosition,
107109
fore: forePosition,
@@ -112,7 +114,7 @@ class ModalDialogTransition: SelfDismissingTransition {
112114
return [tossable.spring]
113115
}
114116

115-
static func willPresent(fore: UIViewController, dismisser: ViewControllerDismisser) {
117+
func willPresent(fore: UIViewController, dismisser: ViewControllerDismisser) {
116118
let tap = UITapGestureRecognizer()
117119
fore.view.addGestureRecognizer(tap)
118120
dismisser.dismissWhenGestureRecognizerBegins(tap)
@@ -121,3 +123,18 @@ class ModalDialogTransition: SelfDismissingTransition {
121123
dismisser.dismissWhenGestureRecognizerBegins(pan)
122124
}
123125
}
126+
127+
private final class ModalDialogPresentationController: UIPresentationController {
128+
129+
override var frameOfPresentedViewInContainerView: CGRect {
130+
guard let containerView = containerView else {
131+
assertionFailure("Missing container view during frame query.")
132+
return .zero()
133+
}
134+
let size = CGSize(width: 200, height: 200)
135+
return CGRect(x: containerView.bounds.midX - size.width / 2,
136+
y: containerView.bounds.midY - size.height / 2,
137+
width: size.width,
138+
height: size.height)
139+
}
140+
}

0 commit comments

Comments
 (0)