-
Notifications
You must be signed in to change notification settings - Fork 12
/
ZoomAnimation.xm
283 lines (244 loc) · 12 KB
/
ZoomAnimation.xm
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#import <UIKit/UIKit.h>
#import "HSWidgets-core.h"
#import "HSWidgetPageController.h"
#import "HSWidgetViewController.h"
#import "SBIconController.h"
#import "SBIconCoordinate.h"
#import "SBIconListModel.h"
#import "SBIconListView.h"
#import "SBRootFolderController.h"
@interface AnimationSettings // iOS 7 - 12 (SBAnimationSettings), iOS 13 (SBFAnimationSettings)
-(id)BSAnimationSettings; // iOS 8 - 13
@property (assign,nonatomic) CGFloat mass; // iOS 7 - 13
@property (assign,nonatomic) NSTimeInterval delay; // iOS 7 - 13
@end
@interface CenterZoomSettings // iOS 7 - 12 (SBCenterZoomSettings), iOS 13 (SBHCenterZoomSettings)
@property (assign) CGFloat centerRowCoordinate; // iOS 7 - 13
-(AnimationSettings *)centralAnimationSettings; // inherited
-(NSInteger)distanceEffect; // iOS 7 - 13
-(CGFloat)firstHopIncrement; // iOS 7 - 13
-(double)hopIncrementAcceleration; // iOS 7 - 13
@end
@interface SBCenterIconZoomAnimator : NSObject // iOS 7 - 13
@property (nonatomic,retain) CenterZoomSettings *settings; // iOS 7 - 13
@property (nonatomic,readonly) SBIconListView *iconListView; // inherited
@property (nonatomic,readonly) UIView * zoomView; // iOS 7 - 13
-(CGFloat)_iconZoomDelay; // iOS 7 - 13
-(CGPoint)cameraPosition; // iOS 7 - 13
-(id)_animationFactoryForWidget:(id)widgetViewController;
@end
@interface SBCoverSheetIconFlyInAnimator : SBCenterIconZoomAnimator // iOS 11 - 13
@end
@interface BSAnimationSettings // iOS 8 - 13
-(void)applyToCAAnimation:(id)arg1; // iOS 9 - 13
-(void)_setDelay:(NSTimeInterval)arg1; // iOS 8 - 13
-(NSTimeInterval)delay; // iOS 8 - 13
-(void)_setSpeed:(float)arg1; // iOS 10 - 13
-(CGFloat)speed; // iOS 10 - 13
@end
@interface BSUIAnimationFactory // iOS 9 - 13
@property (nonatomic,copy,readonly) BSAnimationSettings *settings; // iOS 9 - 13
+(id)factoryWithSettings:(id)arg1; // iOS 9 - 13
+(CGFloat)globalSlowDownFactor; // iOS 9 - 13
-(void)setAllowsAdditiveAnimations:(BOOL)arg1; // iOS 10 - 13
// +(void)animateWithFactory:(id)arg1 additionalDelay:(NSTimeInterval)arg2 options:(NSUInteger)arg3 actions:(id)arg4 completion:(id)arg5; // iOS 9 - 13
@end
@interface CoverSheetTransitionSettings : NSObject // SBCoverSheetTransitionSettings in iOS 11 - 12 and CSCoverSheetTransitionSettings in iOS 13
-(BOOL)iconsFlyIn; // iOS 11 - 13
@end
@interface SBCoverSheetPresentationManager : NSObject // iOS 11 - 13
+(id)sharedInstance;
-(CoverSheetTransitionSettings *)transitionSettings;; // iOS 13
@end
static inline void ConfigureWidgetsIfNeeded(NSArray *iconListViews) {
for (NSInteger listViewIndex = 0; listViewIndex < iconListViews.count; ++listViewIndex) {
SBIconListView *iconListView = iconListViews[listViewIndex];
HSWidgetPageController *widgetPageController = iconListView.widgetPageController;
[widgetPageController configureWidgetsIfNeededWithIndex:listViewIndex];
[iconListView layoutIconsNow];
}
// send all widgets configured notification
[[NSNotificationCenter defaultCenter] postNotificationName:HSWidgetAllWidgetsConfiguredNotification object:nil userInfo:nil];
}
// fix widgets being lost on respring when reduce motion is enabled or fly in animations are disabled
%hook SBCoverSheetPresentationManager
-(void)_prepareForDismissalTransition {
%orig;
static BOOL isFirstIconAnimationAfterRespring = YES;
if (isFirstIconAnimationAfterRespring && (UIAccessibilityIsReduceMotionEnabled() || ![[self transitionSettings] iconsFlyIn])) {
SBIconController *iconController = [%c(SBIconController) sharedInstance];
SBRootFolderController *rootFolderController = [iconController _rootFolderController];
ConfigureWidgetsIfNeeded(rootFolderController.iconListViews);
isFirstIconAnimationAfterRespring = NO;
}
}
%end
%hook SBCenterIconZoomAnimator
-(void)_prepareAnimation {
%orig;
static BOOL isFirstZoomAnimationAfterRespring = YES;
if (isFirstZoomAnimationAfterRespring) {
SBIconController *iconController = [%c(SBIconController) sharedInstance];
SBRootFolderController *rootFolderController = [iconController _rootFolderController];
ConfigureWidgetsIfNeeded(rootFolderController.iconListViews);
isFirstZoomAnimationAfterRespring = NO;
}
SBIconListModel *model = [self.iconListView valueForKey:@"_model"];
if (model.pageLayoutType != PageTypeNone && model.widgetViewControllers != nil) {
for (HSWidgetViewController *widgetViewController in model.widgetViewControllers) {
[self.zoomView addSubview:[widgetViewController zoomAnimatingView]];
}
}
}
-(void)_cleanupAnimation {
%orig;
SBIconListModel *model = [self.iconListView valueForKey:@"_model"];
if (model.pageLayoutType != PageTypeNone && model.widgetViewControllers != nil) {
for (HSWidgetViewController *widgetViewController in model.widgetViewControllers) {
[[widgetViewController zoomAnimatingView].layer removeAnimationForKey:@"HSWidgetZPosition"];
[widgetViewController clearZoomAnimatingView];
if ([widgetViewController viewStyleForZoomAnimaton] != ZoomAnimationViewStyleDefault) {
widgetViewController.view.alpha = 1.0;
} else {
[self.iconListView addSubview:[widgetViewController zoomAnimatingView]]; // move the view back to root icon list view
}
}
}
}
-(void)_calculateCentersAndCameraPosition {
SBIconListModel *model = [self.iconListView valueForKey:@"_model"];
PageType currentPageType = model.pageLayoutType;
model.pageLayoutType = PageTypeNone;
%orig;
model.pageLayoutType = currentPageType;
if (model.pageLayoutType != PageTypeNone && model.widgetViewControllers != nil) {
CGFloat *_centerRow = &MSHookIvar<CGFloat>(self, "_centerRow");
CGFloat *_centerCol = &MSHookIvar<CGFloat>(self, "_centerCol");
NSInteger centerRow = *_centerRow;
NSInteger centerCol = *_centerCol;
CGFloat offsetY = 0;
for (NSInteger row = centerRow; row > 0; --row) {
BOOL rowContainsWidget = NO;
HSWidgetPosition rowPosition = HSWidgetPositionMake(row, centerCol);
for (HSWidgetViewController *widgetViewController in model.widgetViewControllers) {
if (HSWidgetFrameContainsPosition(widgetViewController.widgetFrame, rowPosition)) {
rowContainsWidget = YES;
break;
}
}
if (rowContainsWidget) {
offsetY += 0.5;
}
}
// update center row to make it look a little better
*_centerRow = *_centerRow - offsetY;
}
}
-(void)_performAnimationToFraction:(CGFloat)arg1 withCentralAnimationSettings:(id)arg2 delay:(NSTimeInterval)arg3 alreadyAnimating:(BOOL)arg4 sharedCompletion:(void (^)(BOOL finished))arg5 {
// TODO: Fix the bug where the widget animation is quicker/less delayed than the icon animaiton (seems to be an iOS issue)
SBIconListModel *model = [self.iconListView valueForKey:@"_model"];
if (model.pageLayoutType != PageTypeNone && model.widgetViewControllers != nil) {
NSTimeInterval additionalDelay = arg3 + [self _iconZoomDelay];
CGFloat iconZoomedZ = MSHookIvar<CGFloat>(self, "_iconZoomedZ");
for (HSWidgetViewController *widgetViewController in model.widgetViewControllers) {
/* // doesn't animate since zPosition isn't animated by default but this is the approach used by apple
[%c(BSUIAnimationFactory) animateWithFactory:[self _animationFactoryForWidget:widgetViewController] additionalDelay:additionalDelay options:2 actions:^{
[widgetViewController zoomAnimatingView].layer.zPosition = arg1 * iconZoomedZ;
} completion:arg5];*/
BSUIAnimationFactory *animationFactory = [self _animationFactoryForWidget:widgetViewController];
BSAnimationSettings *animationSettings = animationFactory.settings;
[animationSettings _setSpeed:1.0 / [%c(BSUIAnimationFactory) globalSlowDownFactor]];
[animationSettings _setDelay:[animationSettings delay] + additionalDelay];
[CATransaction begin];
[CATransaction setCompletionBlock:^{
if ([widgetViewController viewStyleForZoomAnimaton] != ZoomAnimationViewStyleDefault) {
if (arg1 == 0) {
widgetViewController.view.alpha = 1.0;
}
[widgetViewController zoomAnimatingView].alpha = 0.0;
}
if (arg5 != nil) {
arg5(YES);
}
}];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"zPosition"];
[animationSettings applyToCAAnimation:animation];
animation.fromValue = @((1 - arg1) * iconZoomedZ);
animation.toValue = @(arg1 * iconZoomedZ);
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
[[widgetViewController zoomAnimatingView].layer addAnimation:animation forKey:@"HSWidgetZPosition"];
[CATransaction commit];
}
}
%orig;
}
-(NSUInteger)_numberOfSignificantAnimations {
NSUInteger result = %orig;
SBIconListModel *model = [self.iconListView valueForKey:@"_model"];
if (model.pageLayoutType != PageTypeNone && model.widgetViewControllers != nil) {
result += [model.widgetViewControllers count];
}
return result;
}
-(void)_setAnimationFraction:(CGFloat)arg1 {
SBIconListModel *model = [self.iconListView valueForKey:@"_model"];
if (model.pageLayoutType != PageTypeNone && model.widgetViewControllers != nil && ![self respondsToSelector:@selector(_setAnimationFraction:withCenter:)]) {
for (HSWidgetViewController *widgetViewController in model.widgetViewControllers) {
[widgetViewController zoomAnimatingView].layer.zPosition = arg1 * MSHookIvar<CGFloat>(self, "_iconZoomedZ");
if ([widgetViewController viewStyleForZoomAnimaton] != ZoomAnimationViewStyleDefault) {
widgetViewController.view.alpha = 0.0;
[widgetViewController zoomAnimatingView].alpha = 1.0;
}
[[widgetViewController zoomAnimatingView].layer removeAnimationForKey:@"HSWidgetZPosition"];
}
}
%orig(arg1);
}
-(void)_setAnimationFraction:(CGFloat)arg1 withCenter:(CGPoint)arg2 {
// On iOS 11+, SBCoverSheetPresentationManager manages the icon animator which creates a custom function to animate zPosition
SBIconListModel *model = [self.iconListView valueForKey:@"_model"];
if (model.pageLayoutType != PageTypeNone && model.widgetViewControllers != nil) {
for (HSWidgetViewController *widgetViewController in model.widgetViewControllers) {
[widgetViewController zoomAnimatingView].layer.zPosition = arg1 * MSHookIvar<CGFloat>(self, "_iconZoomedZ");
if ([widgetViewController viewStyleForZoomAnimaton] != ZoomAnimationViewStyleDefault) {
widgetViewController.view.alpha = 0.0;
[widgetViewController zoomAnimatingView].alpha = 1.0;
}
[[widgetViewController zoomAnimatingView].layer removeAnimationForKey:@"HSWidgetZPosition"];
}
}
%orig(arg1, arg2);
}
%new
-(id)_animationFactoryForWidget:(HSWidgetViewController *)widgetViewController {
AnimationSettings *animationSettings = [self.settings centralAnimationSettings];
CGFloat distanceEffect = [self.settings distanceEffect];
if (distanceEffect > 0 && widgetViewController != nil) {
HSWidgetFrame widgetFrame = widgetViewController.widgetFrame;
CGFloat widgetCenterRow = (widgetFrame.origin.row + widgetFrame.size.numRows) / 2.0;
CGFloat widgetCenterCol = (widgetFrame.origin.col + widgetFrame.size.numCols) / 2.0;
CGFloat diffRow = fabs(widgetCenterRow - MSHookIvar<CGFloat>(self, "_centerRow"));
CGFloat diffCol = fabs(widgetCenterCol - MSHookIvar<CGFloat>(self, "_centerCol"));
NSInteger totalIncrements = floorf(diffRow + diffCol);
CGFloat currentIncrement = [self.settings firstHopIncrement];
CGFloat currentMass = animationSettings.mass;
if (currentIncrement > 0.0 && totalIncrements != 0) {
NSInteger i = 1;
do {
currentMass += currentIncrement * distanceEffect;
currentIncrement += [self.settings hopIncrementAcceleration];
++i;
} while (currentIncrement > 0.0 && i < totalIncrements);
}
CGFloat newMass = MAX(currentMass, 0.1);
if (newMass != animationSettings.mass) {
animationSettings.mass = newMass;
}
}
BSUIAnimationFactory *animationFactory = [%c(BSUIAnimationFactory) factoryWithSettings:[animationSettings BSAnimationSettings]];
[animationFactory setAllowsAdditiveAnimations:YES];
return animationFactory;
}
%end