Skip to content

Commit

Permalink
feat(ios): refreshWrapper support viewPager
Browse files Browse the repository at this point in the history
  • Loading branch information
wwwcg committed Jul 29, 2024
1 parent e1207fe commit faf2483
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 47 deletions.
19 changes: 19 additions & 0 deletions ios/sdk/component/refreshview/HippyRefreshWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,31 @@
*/

#import <UIKit/UIKit.h>
#import "HippyComponent.h"
#import "HippyInvalidating.h"

NS_ASSUME_NONNULL_BEGIN

@class HippyBridge;

/// RefreshWrapper add refresh capability to scrollable components such as ListView
@interface HippyRefreshWrapper : UIView <HippyInvalidating>

/// Direction of Refresh
@property (nonatomic, assign, getter=isHorizontal) BOOL horizontal;

/// Bounce time of refresh start/end animation
@property (nonatomic, assign) CGFloat bounceTime;

/// The onRefresh block that JS side binding.
@property (nonatomic, copy) HippyDirectEventBlock onRefresh;

/// Call to indicate refresh completion.
- (void)refreshCompleted;

/// Call to start the refresh process.
- (void)startRefresh;

@end

NS_ASSUME_NONNULL_END
114 changes: 78 additions & 36 deletions ios/sdk/component/refreshview/HippyRefreshWrapper.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,75 +24,117 @@
#import "UIView+Hippy.h"
#import "HippyRefreshWrapperItemView.h"
#import "HippyScrollableProtocol.h"


static NSTimeInterval const kHippyDefaultRefreshBounceTime = 400.0;

@interface HippyRefreshWrapper () <UIScrollViewDelegate>

/// The child view of RefreshWrapper
@property (nonatomic, weak) HippyRefreshWrapperItemView *wrapperItemView;
/// Scrollable target
@property (nonatomic, weak) id<HippyScrollableProtocol> scrollableView;
@property (nonatomic, copy) HippyDirectEventBlock onRefresh;
@property (nonatomic, assign) CGFloat bounceTime;
@property (nonatomic, weak) HippyBridge *bridge;

@end

@implementation HippyRefreshWrapper

- (void)addSubview:(UIView *)view {
if (view != _wrapperItemView) {
[super addSubview:view];
}
[self refactorViews];
}

- (void)insertHippySubview:(UIView *)view atIndex:(NSInteger)index {
if ([view isKindOfClass:[HippyRefreshWrapperItemView class]]) {
_wrapperItemView = (HippyRefreshWrapperItemView *)view;
} else if ([view conformsToProtocol:@protocol(HippyScrollableProtocol)]) {
_scrollableView = (id<HippyScrollableProtocol>)view;
[_scrollableView addScrollListener:self];
}
[super insertHippySubview:view atIndex:index];
}

- (void)invalidate {
[_scrollableView removeScrollListener:self];
}


#pragma mark - Public & Private Methods

- (void)refactorViews {
if (_wrapperItemView && _scrollableView) {
CGSize size = _wrapperItemView.frame.size;
_wrapperItemView.frame = CGRectMake(0, -size.height, size.width, size.height);
if (self.isHorizontal) {
_wrapperItemView.frame = CGRectMake(-size.width, 0, size.width, size.height);
} else {
_wrapperItemView.frame = CGRectMake(0, -size.height, size.width, size.height);
}
[_scrollableView.realScrollView addSubview:_wrapperItemView];
}
}

- (void)refreshCompleted {
CGFloat duration = _bounceTime != 0 ? _bounceTime : 400;
UIEdgeInsets contentInset = self->_scrollableView.realScrollView.contentInset;
contentInset.top = 0;
[UIView animateWithDuration:duration / 1000.f animations:^{
[self->_scrollableView.realScrollView setContentInset:contentInset];
CGFloat duration = _bounceTime != 0 ? _bounceTime : kHippyDefaultRefreshBounceTime;
UIEdgeInsets contentInset = self.scrollableView.realScrollView.contentInset;
if (self.isHorizontal) {
contentInset.left = 0;
} else {
contentInset.top = 0;
}
[UIView animateWithDuration:duration / 1000.0 animations:^{
[self.scrollableView.realScrollView setContentInset:contentInset];
}];
}

- (void)startRefresh {
CGFloat wrapperItemViewHeight = _wrapperItemView.frame.size.height;
UIEdgeInsets insets = _scrollableView.realScrollView.contentInset;
insets.top = wrapperItemViewHeight;
CGFloat duration = _bounceTime != 0 ? _bounceTime : 400;
[UIView animateWithDuration:duration / 1000.f animations:^{
[self->_scrollableView.realScrollView setContentInset:insets];
[self->_scrollableView.realScrollView setContentOffset:CGPointMake(0, -insets.top)];
CGPoint targetContentOffset;
if (self.isHorizontal) {
CGFloat wrapperItemViewWidth = CGRectGetWidth(_wrapperItemView.frame);
insets.left = wrapperItemViewWidth;
targetContentOffset = CGPointMake(-wrapperItemViewWidth, 0);
} else {
CGFloat wrapperItemViewHeight = CGRectGetHeight(_wrapperItemView.frame);
insets.top = wrapperItemViewHeight;
targetContentOffset = CGPointMake(0, -wrapperItemViewHeight);
}

CGFloat duration = _bounceTime > DBL_EPSILON ? _bounceTime : kHippyDefaultRefreshBounceTime;
[UIView animateWithDuration:duration / 1000.0 animations:^{
[self.scrollableView.realScrollView setContentInset:insets];
[self.scrollableView.realScrollView setContentOffset:targetContentOffset];
}];
if (_onRefresh) {
_onRefresh(@{});
}
}

- (void)insertHippySubview:(UIView *)view atIndex:(NSInteger)index {
if ([view isKindOfClass:[HippyRefreshWrapperItemView class]]) {
_wrapperItemView = (HippyRefreshWrapperItemView *)view;
} else if ([view conformsToProtocol:@protocol(HippyScrollableProtocol)]) {
_scrollableView = (id<HippyScrollableProtocol>)view;
[_scrollableView addScrollListener:self];
}
[super insertHippySubview:view atIndex:index];
}

- (void)invalidate {
[_scrollableView removeScrollListener:self];
}
#pragma mark - ScrollListener, UIScrollViewDelegate

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
CGFloat wrapperItemViewHeight = _wrapperItemView.frame.size.height;
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset {
UIEdgeInsets insets = scrollView.contentInset;
CGFloat contentOffsetY = scrollView.contentOffset.y;
if (contentOffsetY <= -wrapperItemViewHeight && insets.top != wrapperItemViewHeight) {
insets.top = wrapperItemViewHeight;
scrollView.contentInset = insets;
if (_onRefresh) {
_onRefresh(@{});
if (self.isHorizontal) {
CGFloat wrapperItemViewWidth = CGRectGetWidth(_wrapperItemView.frame);
CGFloat contentOffsetX = scrollView.contentOffset.x;
if (contentOffsetX <= -wrapperItemViewWidth && insets.left != wrapperItemViewWidth) {
// Update the end sliding state of scrollview
targetContentOffset->x = -wrapperItemViewWidth;
// start refresh and call js
[self startRefresh];
}
} else {
CGFloat wrapperItemViewHeight = CGRectGetHeight(_wrapperItemView.frame);
CGFloat contentOffsetY = scrollView.contentOffset.y;
if (contentOffsetY <= -wrapperItemViewHeight && insets.top != wrapperItemViewHeight) {
insets.top = wrapperItemViewHeight;
scrollView.contentInset = insets;
if (_onRefresh) {
_onRefresh(@{});
}
}
}
}
Expand Down
8 changes: 1 addition & 7 deletions ios/sdk/component/refreshview/HippyRefreshWrapperItemView.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,7 @@
*/
#import "HippyRefreshWrapperItemView.h"
#import "UIView+Hippy.h"
@implementation HippyRefreshWrapperItemView

- (void)setFrame:(CGRect)frame {
if ([self.superview isKindOfClass:[UIScrollView class]]) {
frame.origin.y = -frame.size.height;
}
[super setFrame:frame];
}
@implementation HippyRefreshWrapperItemView

@end
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ @implementation HippyRefreshWrapperViewManager

HIPPY_EXPORT_MODULE(RefreshWrapper)

HIPPY_EXPORT_VIEW_PROPERTY(horizontal, BOOL)
HIPPY_EXPORT_VIEW_PROPERTY(bounceTime, CGFloat)
HIPPY_EXPORT_VIEW_PROPERTY(onRefresh, HippyDirectEventBlock)

HIPPY_EXPORT_VIEW_PROPERTY(bounceTime, CGFloat)
- (UIView *)view {
return [HippyRefreshWrapper new];
HippyRefreshWrapper *refreshWrapper = [HippyRefreshWrapper new];
return refreshWrapper;
}

HIPPY_EXPORT_METHOD(refreshComplected:(NSNumber *__nonnull)hippyTag args:(id)arg) {
Expand Down
2 changes: 1 addition & 1 deletion ios/sdk/component/viewPager/HippyViewPager.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
*/
typedef void (^ViewPagerItemsCountChanged)(NSUInteger count);

@interface HippyViewPager : UIScrollView <UIScrollViewDelegate, HippyInvalidating>
@interface HippyViewPager : UIScrollView <UIScrollViewDelegate, HippyScrollableProtocol, HippyInvalidating>
@property (nonatomic, strong) HippyDirectEventBlock onPageSelected;
@property (nonatomic, strong) HippyDirectEventBlock onPageScroll;
@property (nonatomic, strong) HippyDirectEventBlock onPageScrollStateChanged;
Expand Down
11 changes: 10 additions & 1 deletion ios/sdk/component/viewPager/HippyViewPager.m
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,16 @@ - (void)scrollViewDidEndScrolling {
self.previousStopOffset = [self contentOffset].x;
}

#pragma mark scrollview listener methods
#pragma mark - scrollview listener methods

- (UIScrollView *)realScrollView {
return self;
}

- (NSHashTable *)scrollListeners {
return _scrollViewListener;
}

- (void)addScrollListener:(id<UIScrollViewDelegate>)scrollListener {
[_scrollViewListener addObject:scrollListener];
}
Expand Down

0 comments on commit faf2483

Please sign in to comment.