Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(iOS): add iOS custom scroll time #3491

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface DemoCustomScrollViewController : UIViewController

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

#import "DemoCustomScrollViewController.h"
#import "HippyCustomScrollView.h"

@interface DemoCustomScrollViewController () <UITableViewDelegate ,UITableViewDataSource>

@property (nonatomic ,strong) UITableView *tableView;

@end

@implementation DemoCustomScrollViewController

- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
}
return _tableView;
}

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}

- (void)click {
[self.tableView setContentOffset:CGPointMake(0, 500) duration:2.25 completion:^{

}];
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
static NSString *identifier = @"identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
cell.textLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.row];
return cell;
}

- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 100;
}

@end
59 changes: 59 additions & 0 deletions framework/examples/ios-demo/HippyDemo/HippyCustomScrollView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

#import <UIKit/UIKit.h>

typedef enum
{
linear = 0,
quadIn,
quadOut,
quadInOut,
cubicIn,
cubicOut,
cubicInOut,
quartIn,
quartOut,
quartInOut,
quintIn,
quintOut,
quintInOut,
sineIn,
sineOut,
sineInOut,
expoIn,
expoOut,
expoInOut,
circleIn,
circleOut,
circleInOut
} HippyScrollTimingEnum;

NS_ASSUME_NONNULL_BEGIN

@class HippyScrollTimingFunction;
@interface UIScrollView (HippyCustomOffsetAnimation)

- (void)setContentOffset:(CGPoint)contentOffset
duration:(NSTimeInterval)duration
completion:(void(^)(void))block;

@end

@interface HippyScrollTimingFunction : NSObject

@property (nonatomic,assign) HippyScrollTimingEnum type;

@end

@interface HippyScrollViewAnimator : NSObject

@property (nonatomic, weak) UIScrollView *scrollView;
@property (nonatomic, copy) void(^block)(void);

- (void)setContentOffset:(CGPoint)contentOffset duration:(NSTimeInterval)duration;
- (instancetype)initWithScrollView:(UIScrollView *)scrollView
timingFunction:(HippyScrollTimingFunction *)timingFunction
type:(HippyScrollTimingEnum)type;

@end

NS_ASSUME_NONNULL_END
214 changes: 214 additions & 0 deletions framework/examples/ios-demo/HippyDemo/HippyCustomScrollView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@

#import "HippyCustomScrollView.h"
#import <objc/runtime.h>

@class HippyScrollViewAnimator;
@implementation UIScrollView (HippyCustomOffsetAnimation)

static NSString *HippyCustomAnimatorKey = @"QModelOverlayParamsKey"; //定义一个key值

- (void)setAnimator:(HippyScrollViewAnimator *)animator
{
objc_setAssociatedObject(self, &HippyCustomAnimatorKey, animator, OBJC_ASSOCIATION_RETAIN);
}

- (HippyScrollViewAnimator *)animator
{
return objc_getAssociatedObject(self, &HippyCustomAnimatorKey);
}

- (void)setContentOffset:(CGPoint)contentOffset
duration:(NSTimeInterval)duration
completion:(void(^)(void))block {
if (!self.animator) {
self.animator = [[HippyScrollViewAnimator alloc] initWithScrollView:self timingFunction:[HippyScrollTimingFunction new] type:sineInOut];
}
__weak __typeof(self) weakSelf = self;
self.animator.block = ^{
__strong __typeof(weakSelf) strongSelf = weakSelf;
dispatch_async(dispatch_get_main_queue(), ^{
if (strongSelf) {
strongSelf.animator = nil;
}
});
block();
};

[self.animator setContentOffset:contentOffset duration:duration];
block();
}

@end

@implementation HippyScrollTimingFunction
///
/// - Parameters:
/// - t: time
/// - b: begin
/// - c: change
/// - d: duration
- (CGFloat)compute:(CGFloat)t b:(CGFloat)b c:(CGFloat)c d:(CGFloat)d {
switch (self.type) {
case linear:
return c * t / d + b;
case quadIn:
t /= d;
return c * t * t + b;
case quadOut:
t /= d;
return -c * t * (t - 2) + b;
case quadInOut:
t /= d / 2;
if (t < 1) {
return c / 2 * t * t + b;
}
t -= 1;
return -c / 2 * (t * (t - 2) - 1) + b;
case cubicIn:
t /= d;
return c * t * t * t + b;
case cubicOut:
t = t / d - 1;
return c * (t * t * t + 1) + b;
case cubicInOut:
t /= d / 2;
if (t < 1) {
return c / 2 * t * t * t + b;
}
t -= 2;
return c / 2 * (t * t * t + 2) + b;
case quartIn:
t /= d;
return c * t * t * t * t + b;
case quartOut:
t = t / d - 1;
return -c * (t * t * t * t - 1) + b;
case quartInOut:
t /= d / 2;
if (t < 1) {
return c / 2 * t * t * t * t + b;
}
t -= 2;
return -c / 2 * (t * t * t * t - 2) + b;
case quintIn:
t /= d;
return c * t * t * t * t * t + b;
case quintOut:
t = t / d - 1;
return c * ( t * t * t * t * t + 1) + b;
case quintInOut:
t /= d / 2;
if (t < 1) {
return c / 2 * t * t * t * t * t + b;
}
t -= 2;
return c / 2 * (t * t * t * t * t + 2) + b;
case sineIn:
return -c * cos(t / d * (M_PI / 2)) + c + b;
case sineOut:
return c * sin(t / d * (M_PI / 2)) + b;
case sineInOut:
return -c / 2 * (cos(M_PI * t / d) - 1) + b;
case expoIn:
return (t == 0) ? b : c * pow(2, 10 * (t / d - 1)) + b;
case expoOut:
return (t == d) ? b + c : c * (-pow(2, -10 * t / d) + 1) + b;
case expoInOut:
if (t == 0) {
return b;
}
if (t == d) {
return b + c;
}
t /= d / 2;
if (t < 1) {
return c / 2 * pow(2, 10 * (t - 1)) + b;
}
t -= 1;
return c / 2 * (-pow(2, -10 * t) + 2) + b;
case circleIn:
t /= d;
return -c * (sqrt(1 - t * t) - 1) + b;
case circleOut:
t = t / d - 1;
return c * sqrt(1 - t * t) + b;
case circleInOut:
t /= d / 2;
if (t < 1) {
return -c / 2 * (sqrt(1 - t * t) - 1) + b;
}
t -= 2;
return c / 2 * (sqrt(1 - t * t) + 1) + b;
}
}

@end

@interface HippyScrollViewAnimator ()

@property (nonatomic, assign) HippyScrollTimingEnum type;
@property (nonatomic, strong) HippyScrollTimingFunction *timingFunction;
@property (nonatomic, assign) NSTimeInterval startTime;
@property (nonatomic, assign) CGPoint startOffset;
@property (nonatomic, assign) CGPoint destinationOffset;
@property (nonatomic, assign) NSTimeInterval duration;
@property (nonatomic, assign) NSTimeInterval runTime;
@property (nonatomic, strong) CADisplayLink *timer;

@end

@implementation HippyScrollViewAnimator

- (instancetype)initWithScrollView:(UIScrollView *)scrollView
timingFunction:(HippyScrollTimingFunction *)timingFunction
type:(HippyScrollTimingEnum)type {
if (self = [super init]) {
self.scrollView = scrollView;
self.timingFunction = timingFunction;
self.type = type;
}
return self;
}

- (void)setContentOffset:(CGPoint)contentOffset duration:(NSTimeInterval)duration {

if (!self.scrollView) return;

self.startTime = [[NSDate date] timeIntervalSince1970];
self.startOffset = self.scrollView.contentOffset;
self.destinationOffset = contentOffset;
self.duration = duration;
self.runTime = 0;

if (self.duration <= 0) {
[self.scrollView setContentOffset:contentOffset animated:NO];
return;
}
if (!self.timer) {
self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(animtedScroll)];
[self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
}

- (void)animtedScroll {

if (!self.timer) return;
if (!self.scrollView) return;

self.runTime += self.timer.duration;

if (self.runTime >= self.duration) {
[self.scrollView setContentOffset:self.destinationOffset animated:NO];
[self.timer invalidate];
self.timer = nil;
self.block();
return;
}

CGPoint offset = self.scrollView.contentOffset;
offset.x = [self.timingFunction compute:self.runTime b:self.startOffset.x c:self.destinationOffset.x - self.startOffset.x d:self.duration];
offset.y = [self.timingFunction compute:self.runTime b:self.startOffset.y c:self.destinationOffset.y - self.startOffset.y d:self.duration];
[self.scrollView setContentOffset:offset animated:NO];
}

@end
Loading