作者 周泳恩

Init

  1 +*.pbxproj -text
  1 +
  2 +# OSX
  3 +#
  4 +.DS_Store
  5 +
  6 +# node.js
  7 +#
  8 +node_modules/
  9 +npm-debug.log
  10 +yarn-error.log
  11 +
  12 +
  13 +# Xcode
  14 +#
  15 +build/
  16 +*.pbxuser
  17 +!default.pbxuser
  18 +*.mode1v3
  19 +!default.mode1v3
  20 +*.mode2v3
  21 +!default.mode2v3
  22 +*.perspectivev3
  23 +!default.perspectivev3
  24 +xcuserdata
  25 +*.xccheckout
  26 +*.moved-aside
  27 +DerivedData
  28 +*.hmap
  29 +*.ipa
  30 +*.xcuserstate
  31 +project.xcworkspace
  32 +
  1 +
  2 +# react-native-vode-mj-refresh
  3 +
  4 +## Getting started
  5 +
  6 +`$ npm install react-native-vode-mj-refresh --save`
  7 +
  8 +### Mostly automatic installation
  9 +
  10 +`$ react-native link react-native-vode-mj-refresh`
  11 +
  12 +### Manual installation
  13 +
  14 +
  15 +#### iOS
  16 +
  17 +1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]`
  18 +2. Go to `node_modules` ➜ `react-native-vode-mj-refresh` and add `RNVodeMjRefresh.xcodeproj`
  19 +3. In XCode, in the project navigator, select your project. Add `libRNVodeMjRefresh.a` to your project's `Build Phases` ➜ `Link Binary With Libraries`
  20 +4. Run your project (`Cmd+R`)<
  21 +
  22 +
  23 +## Usage
  24 +```javascript
  25 +import RNVodeMjRefresh from 'react-native-vode-mj-refresh';
  26 +
  27 +// TODO: What to do with the module?
  28 +RNVodeMjRefresh;
  29 +```
  30 +
  1 +
  2 +import { NativeModules } from 'react-native';
  3 +
  4 +const { RNVodeMjRefresh } = NativeModules;
  5 +
  6 +export default RNVodeMjRefresh;
  1 +//
  2 +// RCTMJRefreshHeader.h
  3 +// RCTMJRefreshHeader
  4 +//
  5 +// Created by Macbook on 2018/6/25.
  6 +// Copyright © 2018年 Macbook. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +#import "MJRefresh.h"
  11 +
  12 +@interface RCTMJRefreshHeader : MJRefreshHeader
  13 +/** 普通闲置状态 */
  14 +//MJRefreshStateIdle = 1,
  15 +/** 松开就可以进行刷新的状态 */
  16 +//MJRefreshStatePulling,
  17 +/** 正在刷新中的状态 */
  18 +//MJRefreshStateRefreshing,
  19 +/** 即将刷新的状态 */
  20 +//MJRefreshStateWillRefresh,
  21 +@property (nonatomic, copy) RCTBubblingEventBlock onMJRefreshIdle;
  22 +@property (nonatomic, copy) RCTBubblingEventBlock onMJWillRefresh;
  23 +@property (nonatomic, copy) RCTBubblingEventBlock onMJRefresh;
  24 +@property (nonatomic, copy) RCTBubblingEventBlock onMJReleaseToRefresh;
  25 +@property (nonatomic, copy) RCTBubblingEventBlock onMJPulling;
  26 +@end
  1 +//
  2 +// RCTMJRefreshHeader.m
  3 +// RCTMJRefreshHeader
  4 +//
  5 +// Created by Macbook on 2018/6/25.
  6 +// Copyright © 2018年 Macbook. All rights reserved.
  7 +//
  8 +
  9 +#import "RCTMJRefreshHeader.h"
  10 +
  11 +@implementation RCTMJRefreshHeader
  12 +- (void)setState:(MJRefreshState)state
  13 +{
  14 + MJRefreshCheckState;
  15 +
  16 + switch (state) {
  17 + case MJRefreshStateIdle:
  18 + if(self.reactTag)self.onMJRefreshIdle(@{@"target": self.reactTag});
  19 + break;
  20 + case MJRefreshStatePulling:
  21 + if(self.reactTag)self.onMJReleaseToRefresh(@{@"target":self.reactTag});
  22 + break;
  23 + case MJRefreshStateRefreshing:
  24 + if(self.reactTag)self.onMJRefresh(@{@"target":self.reactTag});
  25 + break;
  26 + default:
  27 + break;
  28 + }
  29 +}
  30 +- (void)setPullingPercent:(CGFloat)pullingPercent
  31 +{
  32 + [super setPullingPercent:pullingPercent];
  33 + if(self.reactTag)self.onMJPulling(@{@"target":self.reactTag,@"percent":[NSNumber numberWithFloat:pullingPercent]});
  34 +}
  35 +
  36 +@end
  1 +//
  2 +// RCTMJRefreshViewManager.m
  3 +// React
  4 +//
  5 +// Created by Macbook on 2018/6/21.
  6 +// Copyright © 2018年 Facebook. All rights reserved.
  7 +//
  8 +#import "MJRefresh.h"
  9 +#import <React/RCTViewManager.h>
  10 +#import <Foundation/Foundation.h>
  11 +#import "RCTMJRefreshHeader.h"
  12 +#import <React/RCTBridge.h>
  13 +#import <React/RCTUIManager.h>
  14 +#import <React/UIView+React.h>
  15 +
  16 +@interface RCTMJRefreshViewManager:RCTViewManager
  17 +@end
  18 +
  19 +@implementation RCTMJRefreshViewManager
  20 +{
  21 + RCTMJRefreshHeader *header;
  22 +}
  23 +
  24 +RCT_EXPORT_MODULE()
  25 +
  26 +-(UIView *)view
  27 +{
  28 + header=[RCTMJRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)];
  29 + return header;
  30 +}
  31 +- (NSArray *)customDirectEventTypes
  32 +{
  33 + return @[
  34 + @"onMJRefresh",
  35 + @"onMJReleaseToRefresh",
  36 + @"onMJRefreshIdle",
  37 + @"onMJPulling"
  38 + ];
  39 +}
  40 +RCT_EXPORT_VIEW_PROPERTY(onMJRefresh, RCTBubblingEventBlock);
  41 +RCT_EXPORT_VIEW_PROPERTY(onMJRefreshIdle, RCTBubblingEventBlock);
  42 +RCT_EXPORT_VIEW_PROPERTY(onMJReleaseToRefresh, RCTBubblingEventBlock);
  43 +RCT_EXPORT_VIEW_PROPERTY(onMJPulling, RCTBubblingEventBlock);
  44 +
  45 +RCT_EXPORT_METHOD(finishRefresh:(nonnull NSNumber *)reactTag)
  46 +{
  47 + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTMJRefreshHeader *> *viewRegistry) {
  48 + RCTMJRefreshHeader *view = viewRegistry[reactTag];
  49 + if (![view isKindOfClass:[RCTMJRefreshHeader class]]) {
  50 + RCTLogError(@"Invalid view returned from registry, expecting RCTBarrage, got: %@", view);
  51 + } else {
  52 + [view endRefreshing];
  53 + }
  54 + }];
  55 +}
  56 +RCT_EXPORT_METHOD(beginRefresh:(nonnull NSNumber *)reactTag)
  57 +{
  58 + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTMJRefreshHeader *> *viewRegistry) {
  59 + RCTMJRefreshHeader *view = viewRegistry[reactTag];
  60 + if (![view isKindOfClass:[RCTMJRefreshHeader class]]) {
  61 + RCTLogError(@"Invalid view returned from registry, expecting RCTBarrage, got: %@", view);
  62 + } else {
  63 + [view beginRefreshing];
  64 + }
  65 + }];
  66 +}
  67 +-(void)loadNewData
  68 +{
  69 +
  70 +}
  71 +- (dispatch_queue_t)methodQueue
  72 +{
  73 + return dispatch_get_main_queue();
  74 +}
  75 +@end
  1 +//
  2 +// RCTMJScrollContentShadowView.h
  3 +// RCTMJRefreshHeader
  4 +//
  5 +// Created by Macbook on 2018/7/6.
  6 +// Copyright © 2018年 Macbook. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +
  11 +#import <React/RCTShadowView.h>
  12 +
  13 +@interface RCTMJScrollContentShadowView : RCTShadowView
  14 +
  15 +@end
  1 +/**
  2 + * Copyright (c) 2015-present, Facebook, Inc.
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +
  8 +#import "RCTMJScrollContentShadowView.h"
  9 +
  10 +#import <yoga/Yoga.h>
  11 +
  12 +#import <React/RCTUtils.h>
  13 +
  14 +@implementation RCTMJScrollContentShadowView
  15 +
  16 +- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics
  17 + layoutContext:(RCTLayoutContext)layoutContext
  18 +{
  19 + if (layoutMetrics.layoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
  20 + // Motivation:
  21 + // Yoga place `contentView` on the right side of `scrollView` when RTL layout is enfoced.
  22 + // That breaks everything; it is completely pointless to (re)position `contentView`
  23 + // because it is `contentView`'s job. So, we work around it here.
  24 +
  25 + layoutContext.absolutePosition.x += layoutMetrics.frame.size.width;
  26 + layoutMetrics.frame.origin.x = 0;
  27 + }
  28 +
  29 + [super layoutWithMetrics:layoutMetrics layoutContext:layoutContext];
  30 +}
  31 +
  32 +@end
  1 +//
  2 +// RCTMJScrollContentView.h
  3 +// RCTMJRefreshHeader
  4 +//
  5 +// Created by Macbook on 2018/7/6.
  6 +// Copyright © 2018年 Macbook. All rights reserved.
  7 +//
  8 +#import <React/RCTView.h>
  9 +@interface RCTMJScrollContentView : RCTView
  10 +
  11 +@end
  12 +
  1 +//
  2 +// RCTMJScrollContentView.m
  3 +// RCTMJRefreshHeader
  4 +//
  5 +// Created by Macbook on 2018/7/6.
  6 +// Copyright © 2018年 Macbook. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +#import <React/RCTView.h>
  11 +#import <React/RCTAssert.h>
  12 +#import <React/UIView+React.h>
  13 +
  14 +#import "RCTMJScrollView.h"
  15 +#import "RCTMJScrollContentView.h"
  16 +
  17 +
  18 +@implementation RCTMJScrollContentView
  19 +
  20 +- (void)reactSetFrame:(CGRect)frame
  21 +{
  22 + [super reactSetFrame:frame];
  23 +
  24 + RCTMJScrollView *scrollView = (RCTMJScrollView *)self.superview.superview;
  25 +
  26 + if (!scrollView) {
  27 + return;
  28 + }
  29 +
  30 + RCTAssert([scrollView isKindOfClass:[RCTMJScrollView class]],
  31 + @"Unexpected view hierarchy of RCTScrollView component.");
  32 +
  33 + [scrollView updateContentOffsetIfNeeded];
  34 +}
  35 +
  36 +@end
  1 +//
  2 +// RCTMJScrollContentViewMananger.m
  3 +// RCTMJRefreshHeader
  4 +//
  5 +// Created by Macbook on 2018/7/6.
  6 +// Copyright © 2018年 Macbook. All rights reserved.
  7 +//
  8 +
  9 +#import <Foundation/Foundation.h>
  10 +#import <React/RCTViewManager.h>
  11 +
  12 +#import "RCTMJScrollContentShadowView.h"
  13 +#import "RCTMJScrollContentView.h"
  14 +
  15 +@interface RCTMJScrollContentViewManager : RCTViewManager
  16 +
  17 +@end
  18 +
  19 +
  20 +@implementation RCTMJScrollContentViewManager
  21 +
  22 +RCT_EXPORT_MODULE()
  23 +
  24 +- (RCTMJScrollContentView *)view
  25 +{
  26 + return [RCTMJScrollContentView new];
  27 +}
  28 +
  29 +- (RCTShadowView *)shadowView
  30 +{
  31 + return [RCTMJScrollContentShadowView new];
  32 +}
  33 +
  34 +@end
  1 +/**
  2 + * Copyright (c) 2015-present, Facebook, Inc.
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +
  8 +#import <UIKit/UIScrollView.h>
  9 +
  10 +#import <React/RCTAutoInsetsProtocol.h>
  11 +#import <React/RCTEventDispatcher.h>
  12 +#import <React/RCTScrollableProtocol.h>
  13 +#import <React/RCTView.h>
  14 +
  15 +@protocol UIScrollViewDelegate;
  16 +
  17 +@interface RCTMJScrollView : RCTView <UIScrollViewDelegate, RCTScrollableProtocol, RCTAutoInsetsProtocol>
  18 +
  19 +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
  20 +
  21 +/**
  22 + * The `RCTScrollView` may have at most one single subview. This will ensure
  23 + * that the scroll view's `contentSize` will be efficiently set to the size of
  24 + * the single subview's frame. That frame size will be determined somewhat
  25 + * efficiently since it will have already been computed by the off-main-thread
  26 + * layout system.
  27 + */
  28 +@property (nonatomic, readonly) UIView *contentView;
  29 +
  30 +/**
  31 + * If the `contentSize` is not specified (or is specified as {0, 0}, then the
  32 + * `contentSize` will automatically be determined by the size of the subview.
  33 + */
  34 +@property (nonatomic, assign) CGSize contentSize;
  35 +
  36 +/**
  37 + * The underlying scrollView (TODO: can we remove this?)
  38 + */
  39 +@property (nonatomic, readonly) UIScrollView *scrollView;
  40 +
  41 +@property (nonatomic, assign) UIEdgeInsets contentInset;
  42 +@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
  43 +@property (nonatomic, assign) BOOL DEPRECATED_sendUpdatedChildFrames;
  44 +@property (nonatomic, assign) NSTimeInterval scrollEventThrottle;
  45 +@property (nonatomic, assign) BOOL centerContent;
  46 +@property (nonatomic, copy) NSDictionary *maintainVisibleContentPosition;
  47 +@property (nonatomic, assign) int snapToInterval;
  48 +@property (nonatomic, copy) NSString *snapToAlignment;
  49 +
  50 +// NOTE: currently these event props are only declared so we can export the
  51 +// event names to JS - we don't call the blocks directly because scroll events
  52 +// need to be coalesced before sending, for performance reasons.
  53 +@property (nonatomic, copy) RCTDirectEventBlock onScrollBeginDrag;
  54 +@property (nonatomic, copy) RCTDirectEventBlock onScroll;
  55 +@property (nonatomic, copy) RCTDirectEventBlock onScrollEndDrag;
  56 +@property (nonatomic, copy) RCTDirectEventBlock onMomentumScrollBegin;
  57 +@property (nonatomic, copy) RCTDirectEventBlock onMomentumScrollEnd;
  58 +
  59 +@end
  60 +
  61 +@interface RCTMJScrollView (Internal)
  62 +
  63 +- (void)updateContentOffsetIfNeeded;
  64 +
  65 +@end
  66 +
  67 +@interface RCTEventDispatcher (RCTMJScrollView)
  68 +
  69 +/**
  70 + * Send a fake scroll event.
  71 + */
  72 +- (void)sendFakeScrollEvent:(NSNumber *)reactTag;
  73 +
  74 +@end
  75 +
  1 +/**
  2 + * Copyright (c) 2015-present, Facebook, Inc.
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +
  8 +#import "RCTMJScrollView.h"
  9 +
  10 +#import <UIKit/UIKit.h>
  11 +
  12 +#import <React/RCTConvert.h>
  13 +#import <React/RCTEventDispatcher.h>
  14 +#import <React/RCTLog.h>
  15 +#import <React/RCTUIManager.h>
  16 +#import <React/RCTUIManagerObserverCoordinator.h>
  17 +#import <React/RCTUIManagerUtils.h>
  18 +#import <React/RCTUtils.h>
  19 +#import "UIView+Private1.h"
  20 +#import <React/UIView+React.h>
  21 +#import "MJRefresh.h"
  22 +
  23 +#if !TARGET_OS_TV
  24 +#import <React/RCTRefreshControl.h>
  25 +#endif
  26 +
  27 +@interface RCTMJScrollEvent : NSObject <RCTEvent>
  28 +
  29 +- (instancetype)initWithEventName:(NSString *)eventName
  30 + reactTag:(NSNumber *)reactTag
  31 + scrollViewContentOffset:(CGPoint)scrollViewContentOffset
  32 + scrollViewContentInset:(UIEdgeInsets)scrollViewContentInset
  33 + scrollViewContentSize:(CGSize)scrollViewContentSize
  34 + scrollViewFrame:(CGRect)scrollViewFrame
  35 + scrollViewZoomScale:(CGFloat)scrollViewZoomScale
  36 + userData:(NSDictionary *)userData
  37 + coalescingKey:(uint16_t)coalescingKey NS_DESIGNATED_INITIALIZER;
  38 +
  39 +@end
  40 +
  41 +@implementation RCTMJScrollEvent
  42 +{
  43 + CGPoint _scrollViewContentOffset;
  44 + UIEdgeInsets _scrollViewContentInset;
  45 + CGSize _scrollViewContentSize;
  46 + CGRect _scrollViewFrame;
  47 + CGFloat _scrollViewZoomScale;
  48 + NSDictionary *_userData;
  49 + uint16_t _coalescingKey;
  50 +}
  51 +
  52 +@synthesize viewTag = _viewTag;
  53 +@synthesize eventName = _eventName;
  54 +
  55 +- (instancetype)initWithEventName:(NSString *)eventName
  56 + reactTag:(NSNumber *)reactTag
  57 + scrollViewContentOffset:(CGPoint)scrollViewContentOffset
  58 + scrollViewContentInset:(UIEdgeInsets)scrollViewContentInset
  59 + scrollViewContentSize:(CGSize)scrollViewContentSize
  60 + scrollViewFrame:(CGRect)scrollViewFrame
  61 + scrollViewZoomScale:(CGFloat)scrollViewZoomScale
  62 + userData:(NSDictionary *)userData
  63 + coalescingKey:(uint16_t)coalescingKey
  64 +{
  65 + RCTAssertParam(reactTag);
  66 +
  67 + if ((self = [super init])) {
  68 + _eventName = [eventName copy];
  69 + _viewTag = reactTag;
  70 + _scrollViewContentOffset = scrollViewContentOffset;
  71 + _scrollViewContentInset = scrollViewContentInset;
  72 + _scrollViewContentSize = scrollViewContentSize;
  73 + _scrollViewFrame = scrollViewFrame;
  74 + _scrollViewZoomScale = scrollViewZoomScale;
  75 + _userData = userData;
  76 + _coalescingKey = coalescingKey;
  77 + }
  78 + return self;
  79 +}
  80 +
  81 +RCT_NOT_IMPLEMENTED(- (instancetype)init)
  82 +
  83 +- (uint16_t)coalescingKey
  84 +{
  85 + return _coalescingKey;
  86 +}
  87 +
  88 +- (NSDictionary *)body
  89 +{
  90 + NSDictionary *body = @{
  91 + @"contentOffset": @{
  92 + @"x": @(_scrollViewContentOffset.x),
  93 + @"y": @(_scrollViewContentOffset.y)
  94 + },
  95 + @"contentInset": @{
  96 + @"top": @(_scrollViewContentInset.top),
  97 + @"left": @(_scrollViewContentInset.left),
  98 + @"bottom": @(_scrollViewContentInset.bottom),
  99 + @"right": @(_scrollViewContentInset.right)
  100 + },
  101 + @"contentSize": @{
  102 + @"width": @(_scrollViewContentSize.width),
  103 + @"height": @(_scrollViewContentSize.height)
  104 + },
  105 + @"layoutMeasurement": @{
  106 + @"width": @(_scrollViewFrame.size.width),
  107 + @"height": @(_scrollViewFrame.size.height)
  108 + },
  109 + @"zoomScale": @(_scrollViewZoomScale ?: 1),
  110 + };
  111 +
  112 + if (_userData) {
  113 + NSMutableDictionary *mutableBody = [body mutableCopy];
  114 + [mutableBody addEntriesFromDictionary:_userData];
  115 + body = mutableBody;
  116 + }
  117 +
  118 + return body;
  119 +}
  120 +
  121 +- (BOOL)canCoalesce
  122 +{
  123 + return YES;
  124 +}
  125 +
  126 +- (RCTMJScrollEvent *)coalesceWithEvent:(RCTMJScrollEvent *)newEvent
  127 +{
  128 + NSArray<NSDictionary *> *updatedChildFrames = [_userData[@"updatedChildFrames"] arrayByAddingObjectsFromArray:newEvent->_userData[@"updatedChildFrames"]];
  129 + if (updatedChildFrames) {
  130 + NSMutableDictionary *userData = [newEvent->_userData mutableCopy];
  131 + userData[@"updatedChildFrames"] = updatedChildFrames;
  132 + newEvent->_userData = userData;
  133 + }
  134 +
  135 + return newEvent;
  136 +}
  137 +
  138 ++ (NSString *)moduleDotMethod
  139 +{
  140 + return @"RCTEventEmitter.receiveEvent";
  141 +}
  142 +
  143 +- (NSArray *)arguments
  144 +{
  145 + return @[self.viewTag, RCTNormalizeInputEventName(self.eventName), [self body]];
  146 +}
  147 +
  148 +@end
  149 +
  150 +/**
  151 + * Include a custom scroll view subclass because we want to limit certain
  152 + * default UIKit behaviors such as textFields automatically scrolling
  153 + * scroll views that contain them.
  154 + */
  155 +@interface RCTMJCustomScrollView : UIScrollView<UIGestureRecognizerDelegate>
  156 +
  157 +@property (nonatomic, assign) BOOL centerContent;
  158 +#if !TARGET_OS_TV
  159 +@property (nonatomic, strong) RCTRefreshControl *rctRefreshControl;
  160 +@property (nonatomic, assign) BOOL pinchGestureEnabled;
  161 +#endif
  162 +
  163 +@end
  164 +
  165 +
  166 +@implementation RCTMJCustomScrollView
  167 +
  168 +- (instancetype)initWithFrame:(CGRect)frame
  169 +{
  170 + if ((self = [super initWithFrame:frame])) {
  171 + [self.panGestureRecognizer addTarget:self action:@selector(handleCustomPan:)];
  172 +
  173 + if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
  174 + // We intentionaly force `UIScrollView`s `semanticContentAttribute` to `LTR` here
  175 + // because this attribute affects a position of vertical scrollbar; we don't want this
  176 + // scrollbar flip because we also flip it with whole `UIScrollView` flip.
  177 + self.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
  178 + }
  179 +
  180 +#if !TARGET_OS_TV
  181 + _pinchGestureEnabled = YES;
  182 +#endif
  183 + }
  184 + return self;
  185 +}
  186 +
  187 +- (UIView *)contentView
  188 +{
  189 + return ((RCTMJScrollView *)self.superview).contentView;
  190 +}
  191 +
  192 +/**
  193 + * @return Whether or not the scroll view interaction should be blocked because
  194 + * JS was found to be the responder.
  195 + */
  196 +- (BOOL)_shouldDisableScrollInteraction
  197 +{
  198 + // Since this may be called on every pan, we need to make sure to only climb
  199 + // the hierarchy on rare occasions.
  200 + UIView *JSResponder = [RCTUIManager JSResponder];
  201 + if (JSResponder && JSResponder != self.superview) {
  202 + BOOL superviewHasResponder = [self isDescendantOfView:JSResponder];
  203 + return superviewHasResponder;
  204 + }
  205 + return NO;
  206 +}
  207 +
  208 +- (void)handleCustomPan:(__unused UIPanGestureRecognizer *)sender
  209 +{
  210 + if ([self _shouldDisableScrollInteraction] && ![[RCTUIManager JSResponder] isKindOfClass:[RCTMJScrollView class]]) {
  211 + self.panGestureRecognizer.enabled = NO;
  212 + self.panGestureRecognizer.enabled = YES;
  213 + // TODO: If mid bounce, animate the scroll view to a non-bounced position
  214 + // while disabling (but only if `stopScrollInteractionIfJSHasResponder` was
  215 + // called *during* a `pan`). Currently, it will just snap into place which
  216 + // is not so bad either.
  217 + // Another approach:
  218 + // self.scrollEnabled = NO;
  219 + // self.scrollEnabled = YES;
  220 + }
  221 +}
  222 +
  223 +- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
  224 +{
  225 + // Limiting scroll area to an area where we actually have content.
  226 + CGSize contentSize = self.contentSize;
  227 + UIEdgeInsets contentInset = self.contentInset;
  228 + CGSize fullSize = CGSizeMake(
  229 + contentSize.width + contentInset.left + contentInset.right,
  230 + contentSize.height + contentInset.top + contentInset.bottom);
  231 +
  232 + rect = CGRectIntersection((CGRect){CGPointZero, fullSize}, rect);
  233 + if (CGRectIsNull(rect)) {
  234 + return;
  235 + }
  236 +
  237 + [super scrollRectToVisible:rect animated:animated];
  238 +}
  239 +
  240 +/**
  241 + * Returning `YES` cancels touches for the "inner" `view` and causes a scroll.
  242 + * Returning `NO` causes touches to be directed to that inner view and prevents
  243 + * the scroll view from scrolling.
  244 + *
  245 + * `YES` -> Allows scrolling.
  246 + * `NO` -> Doesn't allow scrolling.
  247 + *
  248 + * By default this returns NO for all views that are UIControls and YES for
  249 + * everything else. What that does is allows scroll views to scroll even when a
  250 + * touch started inside of a `UIControl` (`UIButton` etc). For React scroll
  251 + * views, we want the default to be the same behavior as `UIControl`s so we
  252 + * return `YES` by default. But there's one case where we want to block the
  253 + * scrolling no matter what: When JS believes it has its own responder lock on
  254 + * a view that is *above* the scroll view in the hierarchy. So we abuse this
  255 + * `touchesShouldCancelInContentView` API in order to stop the scroll view from
  256 + * scrolling in this case.
  257 + *
  258 + * We are not aware of *any* other solution to the problem because alternative
  259 + * approaches require that we disable the scrollview *before* touches begin or
  260 + * move. This approach (`touchesShouldCancelInContentView`) works even if the
  261 + * JS responder is set after touches start/move because
  262 + * `touchesShouldCancelInContentView` is called as soon as the scroll view has
  263 + * been touched and dragged *just* far enough to decide to begin the "drag"
  264 + * movement of the scroll interaction. Returning `NO`, will cause the drag
  265 + * operation to fail.
  266 + *
  267 + * `touchesShouldCancelInContentView` will stop the *initialization* of a
  268 + * scroll pan gesture and most of the time this is sufficient. On rare
  269 + * occasion, the scroll gesture would have already initialized right before JS
  270 + * notifies native of the JS responder being set. In order to recover from that
  271 + * timing issue we have a fallback that kills any ongoing pan gesture that
  272 + * occurs when native is notified of a JS responder.
  273 + *
  274 + * Note: Explicitly returning `YES`, instead of relying on the default fixes
  275 + * (at least) one bug where if you have a UIControl inside a UIScrollView and
  276 + * tap on the UIControl and then start dragging (to scroll), it won't scroll.
  277 + * Chat with @andras for more details.
  278 + *
  279 + * In order to have this called, you must have delaysContentTouches set to NO
  280 + * (which is the not the `UIKit` default).
  281 + */
  282 +- (BOOL)touchesShouldCancelInContentView:(__unused UIView *)view
  283 +{
  284 + //TODO: shouldn't this call super if _shouldDisableScrollInteraction returns NO?
  285 + return ![self _shouldDisableScrollInteraction];
  286 +}
  287 +
  288 +/*
  289 + * Automatically centers the content such that if the content is smaller than the
  290 + * ScrollView, we force it to be centered, but when you zoom or the content otherwise
  291 + * becomes larger than the ScrollView, there is no padding around the content but it
  292 + * can still fill the whole view.
  293 + */
  294 +- (void)setContentOffset:(CGPoint)contentOffset
  295 +{
  296 + UIView *contentView = [self contentView];
  297 + if (contentView && _centerContent) {
  298 + CGSize subviewSize = contentView.frame.size;
  299 + CGSize scrollViewSize = self.bounds.size;
  300 + if (subviewSize.width <= scrollViewSize.width) {
  301 + contentOffset.x = -(scrollViewSize.width - subviewSize.width) / 2.0;
  302 + }
  303 + if (subviewSize.height <= scrollViewSize.height) {
  304 + contentOffset.y = -(scrollViewSize.height - subviewSize.height) / 2.0;
  305 + }
  306 + }
  307 + super.contentOffset = contentOffset;
  308 +}
  309 +
  310 +- (void)setFrame:(CGRect)frame
  311 +{
  312 + // Preserving and revalidating `contentOffset`.
  313 + CGPoint originalOffset = self.contentOffset;
  314 +
  315 + [super setFrame:frame];
  316 +
  317 + UIEdgeInsets contentInset = self.contentInset;
  318 + CGSize contentSize = self.contentSize;
  319 +
  320 + // If contentSize has not been measured yet we can't check bounds.
  321 + if (CGSizeEqualToSize(contentSize, CGSizeZero)) {
  322 + self.contentOffset = originalOffset;
  323 + } else {
  324 + // Make sure offset don't exceed bounds. This could happen on screen rotation.
  325 + CGSize boundsSize = self.bounds.size;
  326 + self.contentOffset = CGPointMake(
  327 + MAX(-contentInset.left, MIN(contentSize.width - boundsSize.width + contentInset.right, originalOffset.x)),
  328 + MAX(-contentInset.top, MIN(contentSize.height - boundsSize.height + contentInset.bottom, originalOffset.y)));
  329 + }
  330 +}
  331 +
  332 +#if !TARGET_OS_TV
  333 +- (void)setRctRefreshControl:(RCTRefreshControl *)refreshControl
  334 +{
  335 + if (_rctRefreshControl) {
  336 + [_rctRefreshControl removeFromSuperview];
  337 + }
  338 + _rctRefreshControl = refreshControl;
  339 + [self addSubview:_rctRefreshControl];
  340 +}
  341 +
  342 +- (void)setPinchGestureEnabled:(BOOL)pinchGestureEnabled
  343 +{
  344 + self.pinchGestureRecognizer.enabled = pinchGestureEnabled;
  345 + _pinchGestureEnabled = pinchGestureEnabled;
  346 +}
  347 +
  348 +- (void)didMoveToWindow
  349 +{
  350 + [super didMoveToWindow];
  351 + // ScrollView enables pinch gesture late in its lifecycle. So simply setting it
  352 + // in the setter gets overriden when the view loads.
  353 + self.pinchGestureRecognizer.enabled = _pinchGestureEnabled;
  354 +}
  355 +#endif //TARGET_OS_TV
  356 +
  357 +@end
  358 +
  359 +@interface RCTMJScrollView () <RCTUIManagerObserver>
  360 +
  361 +@end
  362 +
  363 +@implementation RCTMJScrollView
  364 +{
  365 + RCTEventDispatcher *_eventDispatcher;
  366 + CGRect _prevFirstVisibleFrame;
  367 + __weak UIView *_firstVisibleView;
  368 + RCTMJCustomScrollView *_scrollView;
  369 + UIView *_contentView;
  370 + NSTimeInterval _lastScrollDispatchTime;
  371 + NSMutableArray<NSValue *> *_cachedChildFrames;
  372 + BOOL _allowNextScrollNoMatterWhat;
  373 + CGRect _lastClippedToRect;
  374 + uint16_t _coalescingKey;
  375 + NSString *_lastEmittedEventName;
  376 + NSHashTable *_scrollListeners;
  377 +}
  378 +
  379 +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
  380 +{
  381 + RCTAssertParam(eventDispatcher);
  382 +
  383 + if ((self = [super initWithFrame:CGRectZero])) {
  384 + _eventDispatcher = eventDispatcher;
  385 +
  386 + _scrollView = [[RCTMJCustomScrollView alloc] initWithFrame:CGRectZero];
  387 + _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  388 + _scrollView.delegate = self;
  389 + _scrollView.delaysContentTouches = NO;
  390 +
  391 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
  392 + // `contentInsetAdjustmentBehavior` is only available since iOS 11.
  393 + // We set the default behavior to "never" so that iOS
  394 + // doesn't do weird things to UIScrollView insets automatically
  395 + // and keeps it as an opt-in behavior.
  396 + if ([_scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
  397 + _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  398 + }
  399 +#endif
  400 +
  401 + _automaticallyAdjustContentInsets = YES;
  402 + _DEPRECATED_sendUpdatedChildFrames = NO;
  403 + _contentInset = UIEdgeInsetsZero;
  404 + _contentSize = CGSizeZero;
  405 + _lastClippedToRect = CGRectNull;
  406 +
  407 + _scrollEventThrottle = 0.0;
  408 + _lastScrollDispatchTime = 0;
  409 + _cachedChildFrames = [NSMutableArray new];
  410 +
  411 + _scrollListeners = [NSHashTable weakObjectsHashTable];
  412 +
  413 + [self addSubview:_scrollView];
  414 + }
  415 + return self;
  416 +}
  417 +
  418 +RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
  419 +RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
  420 +
  421 +static inline void RCTApplyTransformationAccordingLayoutDirection(UIView *view, UIUserInterfaceLayoutDirection layoutDirection) {
  422 + view.transform =
  423 + layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight ?
  424 + CGAffineTransformIdentity :
  425 + CGAffineTransformMakeScale(-1, 1);
  426 +}
  427 +
  428 +- (void)setReactLayoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
  429 +{
  430 + [super setReactLayoutDirection:layoutDirection];
  431 +
  432 + RCTApplyTransformationAccordingLayoutDirection(_scrollView, layoutDirection);
  433 + RCTApplyTransformationAccordingLayoutDirection(_contentView, layoutDirection);
  434 +}
  435 +
  436 +- (void)setRemoveClippedSubviews:(__unused BOOL)removeClippedSubviews
  437 +{
  438 + // Does nothing
  439 +}
  440 +
  441 +- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
  442 +{
  443 + [super insertReactSubview:view atIndex:atIndex];
  444 +
  445 + #if !TARGET_OS_TV
  446 + if ([view isKindOfClass:[RCTRefreshControl class]]) {
  447 + [_scrollView setRctRefreshControl:(RCTRefreshControl *)view];
  448 + } else if ([view isKindOfClass:[MJRefreshHeader class]]){
  449 + _scrollView.mj_header = (MJRefreshHeader *)view;
  450 + } else
  451 + #endif
  452 + {
  453 + RCTAssert(_contentView == nil, @"RCTScrollView may only contain a single subview");
  454 + _contentView = view;
  455 + RCTApplyTransformationAccordingLayoutDirection(_contentView, self.reactLayoutDirection);
  456 + [(RCTMJCustomScrollView *)_scrollView addSubview:view];
  457 + }
  458 +}
  459 +
  460 +- (void)removeReactSubview:(UIView *)subview
  461 +{
  462 + [super removeReactSubview:subview];
  463 +
  464 + #if !TARGET_OS_TV
  465 + if ([subview isKindOfClass:[RCTRefreshControl class]]) {
  466 + [_scrollView setRctRefreshControl:nil];
  467 + } else if ([subview isKindOfClass:[MJRefreshHeader class]]){
  468 + _scrollView.mj_header = nil;
  469 + } else
  470 + #endif
  471 + {
  472 + RCTAssert(_contentView == subview, @"Attempted to remove non-existent subview");
  473 + _contentView = nil;
  474 + }
  475 +}
  476 +
  477 +- (void)didUpdateReactSubviews
  478 +{
  479 + // Do nothing, as subviews are managed by `insertReactSubview:atIndex:`
  480 +}
  481 +
  482 +- (void)didSetProps:(NSArray<NSString *> *)changedProps
  483 +{
  484 + if ([changedProps containsObject:@"contentSize"]) {
  485 + [self updateContentOffsetIfNeeded];
  486 + }
  487 +}
  488 +
  489 +- (BOOL)centerContent
  490 +{
  491 + return _scrollView.centerContent;
  492 +}
  493 +
  494 +- (void)setCenterContent:(BOOL)centerContent
  495 +{
  496 + _scrollView.centerContent = centerContent;
  497 +}
  498 +
  499 +- (void)setClipsToBounds:(BOOL)clipsToBounds
  500 +{
  501 + super.clipsToBounds = clipsToBounds;
  502 + _scrollView.clipsToBounds = clipsToBounds;
  503 +}
  504 +
  505 +- (void)dealloc
  506 +{
  507 + _scrollView.delegate = nil;
  508 + if (_maintainVisibleContentPosition != nil) {
  509 + [_eventDispatcher.bridge.uiManager.observerCoordinator removeObserver:self];
  510 + }
  511 +}
  512 +
  513 +- (void)layoutSubviews
  514 +{
  515 + [super layoutSubviews];
  516 + RCTAssert(self.subviews.count == 1, @"we should only have exactly one subview");
  517 + RCTAssert([self.subviews lastObject] == _scrollView, @"our only subview should be a scrollview");
  518 +
  519 +#if !TARGET_OS_TV
  520 + // Adjust the refresh control frame if the scrollview layout changes.
  521 + RCTRefreshControl *refreshControl = _scrollView.rctRefreshControl;
  522 + if (refreshControl && refreshControl.refreshing) {
  523 + refreshControl.frame = (CGRect){_scrollView.contentOffset, {_scrollView.frame.size.width, refreshControl.frame.size.height}};
  524 + }
  525 +#endif
  526 +
  527 + [self updateClippedSubviews];
  528 +}
  529 +
  530 +- (void)updateClippedSubviews
  531 +{
  532 + // Find a suitable view to use for clipping
  533 + UIView *clipView = [self react_findClipView];
  534 + if (!clipView) {
  535 + return;
  536 + }
  537 +
  538 + static const CGFloat leeway = 1.0;
  539 +
  540 + const CGSize contentSize = _scrollView.contentSize;
  541 + const CGRect bounds = _scrollView.bounds;
  542 + const BOOL scrollsHorizontally = contentSize.width > bounds.size.width;
  543 + const BOOL scrollsVertically = contentSize.height > bounds.size.height;
  544 +
  545 + const BOOL shouldClipAgain =
  546 + CGRectIsNull(_lastClippedToRect) ||
  547 + !CGRectEqualToRect(_lastClippedToRect, bounds) ||
  548 + (scrollsHorizontally && (bounds.size.width < leeway || fabs(_lastClippedToRect.origin.x - bounds.origin.x) >= leeway)) ||
  549 + (scrollsVertically && (bounds.size.height < leeway || fabs(_lastClippedToRect.origin.y - bounds.origin.y) >= leeway));
  550 +
  551 + if (shouldClipAgain) {
  552 + const CGRect clipRect = CGRectInset(clipView.bounds, -leeway, -leeway);
  553 + [self react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView];
  554 + _lastClippedToRect = bounds;
  555 + }
  556 +}
  557 +
  558 +- (void)setContentInset:(UIEdgeInsets)contentInset
  559 +{
  560 + if (UIEdgeInsetsEqualToEdgeInsets(contentInset, _contentInset)) {
  561 + return;
  562 + }
  563 +
  564 + CGPoint contentOffset = _scrollView.contentOffset;
  565 +
  566 + _contentInset = contentInset;
  567 + [RCTView autoAdjustInsetsForView:self
  568 + withScrollView:_scrollView
  569 + updateOffset:NO];
  570 +
  571 + _scrollView.contentOffset = contentOffset;
  572 +}
  573 +
  574 +- (BOOL)isHorizontal:(UIScrollView *)scrollView
  575 +{
  576 + return scrollView.contentSize.width > self.frame.size.width;
  577 +}
  578 +
  579 +- (void)scrollToOffset:(CGPoint)offset
  580 +{
  581 + [self scrollToOffset:offset animated:YES];
  582 +}
  583 +
  584 +- (void)scrollToOffset:(CGPoint)offset animated:(BOOL)animated
  585 +{
  586 + if (!CGPointEqualToPoint(_scrollView.contentOffset, offset)) {
  587 + // Ensure at least one scroll event will fire
  588 + _allowNextScrollNoMatterWhat = YES;
  589 + [_scrollView setContentOffset:offset animated:animated];
  590 + }
  591 +}
  592 +
  593 +/**
  594 + * If this is a vertical scroll view, scrolls to the bottom.
  595 + * If this is a horizontal scroll view, scrolls to the right.
  596 + */
  597 +- (void)scrollToEnd:(BOOL)animated
  598 +{
  599 + BOOL isHorizontal = [self isHorizontal:_scrollView];
  600 + CGPoint offset;
  601 + if (isHorizontal) {
  602 + CGFloat offsetX = _scrollView.contentSize.width - _scrollView.bounds.size.width + _scrollView.contentInset.right;
  603 + offset = CGPointMake(fmax(offsetX, 0), 0);
  604 + } else {
  605 + CGFloat offsetY = _scrollView.contentSize.height - _scrollView.bounds.size.height + _scrollView.contentInset.bottom;
  606 + offset = CGPointMake(0, fmax(offsetY, 0));
  607 + }
  608 + if (!CGPointEqualToPoint(_scrollView.contentOffset, offset)) {
  609 + // Ensure at least one scroll event will fire
  610 + _allowNextScrollNoMatterWhat = YES;
  611 + [_scrollView setContentOffset:offset animated:animated];
  612 + }
  613 +}
  614 +
  615 +- (void)zoomToRect:(CGRect)rect animated:(BOOL)animated
  616 +{
  617 + [_scrollView zoomToRect:rect animated:animated];
  618 +}
  619 +
  620 +- (void)refreshContentInset
  621 +{
  622 + [RCTView autoAdjustInsetsForView:self
  623 + withScrollView:_scrollView
  624 + updateOffset:YES];
  625 +}
  626 +
  627 +#pragma mark - ScrollView delegate
  628 +
  629 +#define RCT_SEND_SCROLL_EVENT(_eventName, _userData) { \
  630 +NSString *eventName = NSStringFromSelector(@selector(_eventName)); \
  631 +[self sendScrollEventWithName:eventName scrollView:_scrollView userData:_userData]; \
  632 +}
  633 +
  634 +#define RCT_FORWARD_SCROLL_EVENT(call) \
  635 +for (NSObject<UIScrollViewDelegate> *scrollViewListener in _scrollListeners) { \
  636 +if ([scrollViewListener respondsToSelector:_cmd]) { \
  637 +[scrollViewListener call]; \
  638 +} \
  639 +}
  640 +
  641 +#define RCT_SCROLL_EVENT_HANDLER(delegateMethod, eventName) \
  642 +- (void)delegateMethod:(UIScrollView *)scrollView \
  643 +{ \
  644 +RCT_SEND_SCROLL_EVENT(eventName, nil); \
  645 +RCT_FORWARD_SCROLL_EVENT(delegateMethod:scrollView); \
  646 +}
  647 +
  648 +RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, onMomentumScrollBegin)
  649 +RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, onScroll)
  650 +
  651 +- (void)addScrollListener:(NSObject<UIScrollViewDelegate> *)scrollListener
  652 +{
  653 + [_scrollListeners addObject:scrollListener];
  654 +}
  655 +
  656 +- (void)removeScrollListener:(NSObject<UIScrollViewDelegate> *)scrollListener
  657 +{
  658 + [_scrollListeners removeObject:scrollListener];
  659 +}
  660 +
  661 +- (void)scrollViewDidScroll:(UIScrollView *)scrollView
  662 +{
  663 + [self updateClippedSubviews];
  664 + NSTimeInterval now = CACurrentMediaTime();
  665 + /**
  666 + * TODO: this logic looks wrong, and it may be because it is. Currently, if _scrollEventThrottle
  667 + * is set to zero (the default), the "didScroll" event is only sent once per scroll, instead of repeatedly
  668 + * while scrolling as expected. However, if you "fix" that bug, ScrollView will generate repeated
  669 + * warnings, and behave strangely (ListView works fine however), so don't fix it unless you fix that too!
  670 + */
  671 + if (_allowNextScrollNoMatterWhat ||
  672 + (_scrollEventThrottle > 0 && _scrollEventThrottle < (now - _lastScrollDispatchTime))) {
  673 +
  674 + if (_DEPRECATED_sendUpdatedChildFrames) {
  675 + // Calculate changed frames
  676 + RCT_SEND_SCROLL_EVENT(onScroll, (@{@"updatedChildFrames": [self calculateChildFramesData]}));
  677 + } else {
  678 + RCT_SEND_SCROLL_EVENT(onScroll, nil);
  679 + }
  680 +
  681 + // Update dispatch time
  682 + _lastScrollDispatchTime = now;
  683 + _allowNextScrollNoMatterWhat = NO;
  684 + }
  685 + RCT_FORWARD_SCROLL_EVENT(scrollViewDidScroll:scrollView);
  686 +}
  687 +
  688 +- (NSArray<NSDictionary *> *)calculateChildFramesData
  689 +{
  690 + NSMutableArray<NSDictionary *> *updatedChildFrames = [NSMutableArray new];
  691 + [[_contentView reactSubviews] enumerateObjectsUsingBlock:
  692 + ^(UIView *subview, NSUInteger idx, __unused BOOL *stop) {
  693 +
  694 + // Check if new or changed
  695 + CGRect newFrame = subview.frame;
  696 + BOOL frameChanged = NO;
  697 + if (self->_cachedChildFrames.count <= idx) {
  698 + frameChanged = YES;
  699 + [self->_cachedChildFrames addObject:[NSValue valueWithCGRect:newFrame]];
  700 + } else if (!CGRectEqualToRect(newFrame, [self->_cachedChildFrames[idx] CGRectValue])) {
  701 + frameChanged = YES;
  702 + self->_cachedChildFrames[idx] = [NSValue valueWithCGRect:newFrame];
  703 + }
  704 +
  705 + // Create JS frame object
  706 + if (frameChanged) {
  707 + [updatedChildFrames addObject: @{
  708 + @"index": @(idx),
  709 + @"x": @(newFrame.origin.x),
  710 + @"y": @(newFrame.origin.y),
  711 + @"width": @(newFrame.size.width),
  712 + @"height": @(newFrame.size.height),
  713 + }];
  714 + }
  715 + }];
  716 +
  717 + return updatedChildFrames;
  718 +}
  719 +
  720 +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
  721 +{
  722 + _allowNextScrollNoMatterWhat = YES; // Ensure next scroll event is recorded, regardless of throttle
  723 + RCT_SEND_SCROLL_EVENT(onScrollBeginDrag, nil);
  724 + RCT_FORWARD_SCROLL_EVENT(scrollViewWillBeginDragging:scrollView);
  725 +}
  726 +
  727 +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
  728 +{
  729 + // snapToInterval
  730 + // An alternative to enablePaging which allows setting custom stopping intervals,
  731 + // smaller than a full page size. Often seen in apps which feature horizonally
  732 + // scrolling items. snapToInterval does not enforce scrolling one interval at a time
  733 + // but guarantees that the scroll will stop at an interval point.
  734 + if (self.snapToInterval) {
  735 + CGFloat snapToIntervalF = (CGFloat)self.snapToInterval;
  736 +
  737 + // Find which axis to snap
  738 + BOOL isHorizontal = [self isHorizontal:scrollView];
  739 +
  740 + // What is the current offset?
  741 + CGFloat velocityAlongAxis = isHorizontal ? velocity.x : velocity.y;
  742 + CGFloat targetContentOffsetAlongAxis = isHorizontal ? targetContentOffset->x : targetContentOffset->y;
  743 +
  744 + // Offset based on desired alignment
  745 + CGFloat frameLength = isHorizontal ? self.frame.size.width : self.frame.size.height;
  746 + CGFloat alignmentOffset = 0.0f;
  747 + if ([self.snapToAlignment isEqualToString: @"center"]) {
  748 + alignmentOffset = (frameLength * 0.5f) + (snapToIntervalF * 0.5f);
  749 + } else if ([self.snapToAlignment isEqualToString: @"end"]) {
  750 + alignmentOffset = frameLength;
  751 + }
  752 +
  753 + // Pick snap point based on direction and proximity
  754 + CGFloat fractionalIndex = (targetContentOffsetAlongAxis + alignmentOffset) / snapToIntervalF;
  755 + NSInteger snapIndex =
  756 + velocityAlongAxis > 0.0 ?
  757 + ceil(fractionalIndex) :
  758 + velocityAlongAxis < 0.0 ?
  759 + floor(fractionalIndex) :
  760 + round(fractionalIndex);
  761 + CGFloat newTargetContentOffset = (snapIndex * snapToIntervalF) - alignmentOffset;
  762 +
  763 + // Set new targetContentOffset
  764 + if (isHorizontal) {
  765 + targetContentOffset->x = newTargetContentOffset;
  766 + } else {
  767 + targetContentOffset->y = newTargetContentOffset;
  768 + }
  769 + }
  770 +
  771 + NSDictionary *userData = @{
  772 + @"velocity": @{
  773 + @"x": @(velocity.x),
  774 + @"y": @(velocity.y)
  775 + },
  776 + @"targetContentOffset": @{
  777 + @"x": @(targetContentOffset->x),
  778 + @"y": @(targetContentOffset->y)
  779 + }
  780 + };
  781 + RCT_SEND_SCROLL_EVENT(onScrollEndDrag, userData);
  782 + RCT_FORWARD_SCROLL_EVENT(scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset);
  783 +}
  784 +
  785 +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
  786 +{
  787 + RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndDragging:scrollView willDecelerate:decelerate);
  788 +}
  789 +
  790 +- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
  791 +{
  792 + RCT_SEND_SCROLL_EVENT(onScrollBeginDrag, nil);
  793 + RCT_FORWARD_SCROLL_EVENT(scrollViewWillBeginZooming:scrollView withView:view);
  794 +}
  795 +
  796 +- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
  797 +{
  798 + RCT_SEND_SCROLL_EVENT(onScrollEndDrag, nil);
  799 + RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndZooming:scrollView withView:view atScale:scale);
  800 +}
  801 +
  802 +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
  803 +{
  804 + // Fire a final scroll event
  805 + _allowNextScrollNoMatterWhat = YES;
  806 + [self scrollViewDidScroll:scrollView];
  807 +
  808 + // Fire the end deceleration event
  809 + RCT_SEND_SCROLL_EVENT(onMomentumScrollEnd, nil);
  810 + RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndDecelerating:scrollView);
  811 +}
  812 +
  813 +- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
  814 +{
  815 + // Fire a final scroll event
  816 + _allowNextScrollNoMatterWhat = YES;
  817 + [self scrollViewDidScroll:scrollView];
  818 +
  819 + // Fire the end deceleration event
  820 + RCT_SEND_SCROLL_EVENT(onMomentumScrollEnd, nil);
  821 + RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndScrollingAnimation:scrollView);
  822 +}
  823 +
  824 +- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
  825 +{
  826 + for (NSObject<UIScrollViewDelegate> *scrollListener in _scrollListeners) {
  827 + if ([scrollListener respondsToSelector:_cmd] &&
  828 + ![scrollListener scrollViewShouldScrollToTop:scrollView]) {
  829 + return NO;
  830 + }
  831 + }
  832 + return YES;
  833 +}
  834 +
  835 +- (UIView *)viewForZoomingInScrollView:(__unused UIScrollView *)scrollView
  836 +{
  837 + return _contentView;
  838 +}
  839 +
  840 +#pragma mark - Setters
  841 +
  842 +- (CGSize)_calculateViewportSize
  843 +{
  844 + CGSize viewportSize = self.bounds.size;
  845 + if (_automaticallyAdjustContentInsets) {
  846 + UIEdgeInsets contentInsets = [RCTView contentInsetsForView:self];
  847 + viewportSize = CGSizeMake(self.bounds.size.width - contentInsets.left - contentInsets.right,
  848 + self.bounds.size.height - contentInsets.top - contentInsets.bottom);
  849 + }
  850 + return viewportSize;
  851 +}
  852 +
  853 +- (CGPoint)calculateOffsetForContentSize:(CGSize)newContentSize
  854 +{
  855 + CGPoint oldOffset = _scrollView.contentOffset;
  856 + CGPoint newOffset = oldOffset;
  857 +
  858 + CGSize oldContentSize = _scrollView.contentSize;
  859 + CGSize viewportSize = [self _calculateViewportSize];
  860 +
  861 + BOOL fitsinViewportY = oldContentSize.height <= viewportSize.height && newContentSize.height <= viewportSize.height;
  862 + if (newContentSize.height < oldContentSize.height && !fitsinViewportY) {
  863 + CGFloat offsetHeight = oldOffset.y + viewportSize.height;
  864 + if (oldOffset.y < 0) {
  865 + // overscrolled on top, leave offset alone
  866 + } else if (offsetHeight > oldContentSize.height) {
  867 + // overscrolled on the bottom, preserve overscroll amount
  868 + newOffset.y = MAX(0, oldOffset.y - (oldContentSize.height - newContentSize.height));
  869 + } else if (offsetHeight > newContentSize.height) {
  870 + // offset falls outside of bounds, scroll back to end of list
  871 + newOffset.y = MAX(0, newContentSize.height - viewportSize.height);
  872 + }
  873 + }
  874 +
  875 + BOOL fitsinViewportX = oldContentSize.width <= viewportSize.width && newContentSize.width <= viewportSize.width;
  876 + if (newContentSize.width < oldContentSize.width && !fitsinViewportX) {
  877 + CGFloat offsetHeight = oldOffset.x + viewportSize.width;
  878 + if (oldOffset.x < 0) {
  879 + // overscrolled at the beginning, leave offset alone
  880 + } else if (offsetHeight > oldContentSize.width && newContentSize.width > viewportSize.width) {
  881 + // overscrolled at the end, preserve overscroll amount as much as possible
  882 + newOffset.x = MAX(0, oldOffset.x - (oldContentSize.width - newContentSize.width));
  883 + } else if (offsetHeight > newContentSize.width) {
  884 + // offset falls outside of bounds, scroll back to end
  885 + newOffset.x = MAX(0, newContentSize.width - viewportSize.width);
  886 + }
  887 + }
  888 +
  889 + // all other cases, offset doesn't change
  890 + return newOffset;
  891 +}
  892 +
  893 +/**
  894 + * Once you set the `contentSize`, to a nonzero value, it is assumed to be
  895 + * managed by you, and we'll never automatically compute the size for you,
  896 + * unless you manually reset it back to {0, 0}
  897 + */
  898 +- (CGSize)contentSize
  899 +{
  900 + if (!CGSizeEqualToSize(_contentSize, CGSizeZero)) {
  901 + return _contentSize;
  902 + }
  903 +
  904 + return _contentView.frame.size;
  905 +}
  906 +
  907 +- (void)updateContentOffsetIfNeeded
  908 +{
  909 + CGSize contentSize = self.contentSize;
  910 + if (!CGSizeEqualToSize(_scrollView.contentSize, contentSize)) {
  911 + // When contentSize is set manually, ScrollView internals will reset
  912 + // contentOffset to {0, 0}. Since we potentially set contentSize whenever
  913 + // anything in the ScrollView updates, we workaround this issue by manually
  914 + // adjusting contentOffset whenever this happens
  915 + CGPoint newOffset = [self calculateOffsetForContentSize:contentSize];
  916 + _scrollView.contentSize = contentSize;
  917 + _scrollView.contentOffset = newOffset;
  918 + }
  919 +}
  920 +
  921 +// maintainVisibleContentPosition is used to allow seamless loading of content from both ends of
  922 +// the scrollview without the visible content jumping in position.
  923 +- (void)setMaintainVisibleContentPosition:(NSDictionary *)maintainVisibleContentPosition
  924 +{
  925 + if (maintainVisibleContentPosition != nil && _maintainVisibleContentPosition == nil) {
  926 + [_eventDispatcher.bridge.uiManager.observerCoordinator addObserver:self];
  927 + } else if (maintainVisibleContentPosition == nil && _maintainVisibleContentPosition != nil) {
  928 + [_eventDispatcher.bridge.uiManager.observerCoordinator removeObserver:self];
  929 + }
  930 + _maintainVisibleContentPosition = maintainVisibleContentPosition;
  931 +}
  932 +
  933 +#pragma mark - RCTUIManagerObserver
  934 +
  935 +- (void)uiManagerWillPerformMounting:(RCTUIManager *)manager
  936 +{
  937 + RCTAssertUIManagerQueue();
  938 + [manager prependUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
  939 + BOOL horz = [self isHorizontal:self->_scrollView];
  940 + NSUInteger minIdx = [self->_maintainVisibleContentPosition[@"minIndexForVisible"] integerValue];
  941 + for (NSUInteger ii = minIdx; ii < self->_contentView.subviews.count; ++ii) {
  942 + // Find the first entirely visible view. This must be done after we update the content offset
  943 + // or it will tend to grab rows that were made visible by the shift in position
  944 + UIView *subview = self->_contentView.subviews[ii];
  945 + if ((horz
  946 + ? subview.frame.origin.x >= self->_scrollView.contentOffset.x
  947 + : subview.frame.origin.y >= self->_scrollView.contentOffset.y) ||
  948 + ii == self->_contentView.subviews.count - 1) {
  949 + self->_prevFirstVisibleFrame = subview.frame;
  950 + self->_firstVisibleView = subview;
  951 + break;
  952 + }
  953 + }
  954 + }];
  955 + [manager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
  956 + if (self->_maintainVisibleContentPosition == nil) {
  957 + return; // The prop might have changed in the previous UIBlocks, so need to abort here.
  958 + }
  959 + NSNumber *autoscrollThreshold = self->_maintainVisibleContentPosition[@"autoscrollToTopThreshold"];
  960 + // TODO: detect and handle/ignore re-ordering
  961 + if ([self isHorizontal:self->_scrollView]) {
  962 + CGFloat deltaX = self->_firstVisibleView.frame.origin.x - self->_prevFirstVisibleFrame.origin.x;
  963 + if (ABS(deltaX) > 0.1) {
  964 + self->_scrollView.contentOffset = CGPointMake(
  965 + self->_scrollView.contentOffset.x + deltaX,
  966 + self->_scrollView.contentOffset.y
  967 + );
  968 + if (autoscrollThreshold != nil) {
  969 + // If the offset WAS within the threshold of the start, animate to the start.
  970 + if (self->_scrollView.contentOffset.x - deltaX <= [autoscrollThreshold integerValue]) {
  971 + [self scrollToOffset:CGPointMake(0, self->_scrollView.contentOffset.y) animated:YES];
  972 + }
  973 + }
  974 + }
  975 + } else {
  976 + CGRect newFrame = self->_firstVisibleView.frame;
  977 + CGFloat deltaY = newFrame.origin.y - self->_prevFirstVisibleFrame.origin.y;
  978 + if (ABS(deltaY) > 0.1) {
  979 + self->_scrollView.contentOffset = CGPointMake(
  980 + self->_scrollView.contentOffset.x,
  981 + self->_scrollView.contentOffset.y + deltaY
  982 + );
  983 + if (autoscrollThreshold != nil) {
  984 + // If the offset WAS within the threshold of the start, animate to the start.
  985 + if (self->_scrollView.contentOffset.y - deltaY <= [autoscrollThreshold integerValue]) {
  986 + [self scrollToOffset:CGPointMake(self->_scrollView.contentOffset.x, 0) animated:YES];
  987 + }
  988 + }
  989 + }
  990 + }
  991 + }];
  992 +}
  993 +
  994 +// Note: setting several properties of UIScrollView has the effect of
  995 +// resetting its contentOffset to {0, 0}. To prevent this, we generate
  996 +// setters here that will record the contentOffset beforehand, and
  997 +// restore it after the property has been set.
  998 +
  999 +#define RCT_SET_AND_PRESERVE_OFFSET(setter, getter, type) \
  1000 +- (void)setter:(type)value \
  1001 +{ \
  1002 +CGPoint contentOffset = _scrollView.contentOffset; \
  1003 +[_scrollView setter:value]; \
  1004 +_scrollView.contentOffset = contentOffset; \
  1005 +} \
  1006 +- (type)getter \
  1007 +{ \
  1008 +return [_scrollView getter]; \
  1009 +}
  1010 +
  1011 +RCT_SET_AND_PRESERVE_OFFSET(setAlwaysBounceHorizontal, alwaysBounceHorizontal, BOOL)
  1012 +RCT_SET_AND_PRESERVE_OFFSET(setAlwaysBounceVertical, alwaysBounceVertical, BOOL)
  1013 +RCT_SET_AND_PRESERVE_OFFSET(setBounces, bounces, BOOL)
  1014 +RCT_SET_AND_PRESERVE_OFFSET(setBouncesZoom, bouncesZoom, BOOL)
  1015 +RCT_SET_AND_PRESERVE_OFFSET(setCanCancelContentTouches, canCancelContentTouches, BOOL)
  1016 +RCT_SET_AND_PRESERVE_OFFSET(setDecelerationRate, decelerationRate, CGFloat)
  1017 +RCT_SET_AND_PRESERVE_OFFSET(setDirectionalLockEnabled, isDirectionalLockEnabled, BOOL)
  1018 +RCT_SET_AND_PRESERVE_OFFSET(setIndicatorStyle, indicatorStyle, UIScrollViewIndicatorStyle)
  1019 +RCT_SET_AND_PRESERVE_OFFSET(setKeyboardDismissMode, keyboardDismissMode, UIScrollViewKeyboardDismissMode)
  1020 +RCT_SET_AND_PRESERVE_OFFSET(setMaximumZoomScale, maximumZoomScale, CGFloat)
  1021 +RCT_SET_AND_PRESERVE_OFFSET(setMinimumZoomScale, minimumZoomScale, CGFloat)
  1022 +RCT_SET_AND_PRESERVE_OFFSET(setScrollEnabled, isScrollEnabled, BOOL)
  1023 +#if !TARGET_OS_TV
  1024 +RCT_SET_AND_PRESERVE_OFFSET(setPagingEnabled, isPagingEnabled, BOOL)
  1025 +RCT_SET_AND_PRESERVE_OFFSET(setScrollsToTop, scrollsToTop, BOOL)
  1026 +#endif
  1027 +RCT_SET_AND_PRESERVE_OFFSET(setShowsHorizontalScrollIndicator, showsHorizontalScrollIndicator, BOOL)
  1028 +RCT_SET_AND_PRESERVE_OFFSET(setShowsVerticalScrollIndicator, showsVerticalScrollIndicator, BOOL)
  1029 +RCT_SET_AND_PRESERVE_OFFSET(setZoomScale, zoomScale, CGFloat);
  1030 +RCT_SET_AND_PRESERVE_OFFSET(setScrollIndicatorInsets, scrollIndicatorInsets, UIEdgeInsets);
  1031 +
  1032 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
  1033 +- (void)setContentInsetAdjustmentBehavior:(UIScrollViewContentInsetAdjustmentBehavior)behavior
  1034 +{
  1035 + // `contentInsetAdjustmentBehavior` is available since iOS 11.
  1036 + if ([_scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
  1037 + CGPoint contentOffset = _scrollView.contentOffset;
  1038 + _scrollView.contentInsetAdjustmentBehavior = behavior;
  1039 + _scrollView.contentOffset = contentOffset;
  1040 + }
  1041 +}
  1042 +#endif
  1043 +
  1044 +- (void)sendScrollEventWithName:(NSString *)eventName
  1045 + scrollView:(UIScrollView *)scrollView
  1046 + userData:(NSDictionary *)userData
  1047 +{
  1048 + if (![_lastEmittedEventName isEqualToString:eventName]) {
  1049 + _coalescingKey++;
  1050 + _lastEmittedEventName = [eventName copy];
  1051 + }
  1052 + RCTMJScrollEvent *scrollEvent = [[RCTMJScrollEvent alloc] initWithEventName:eventName
  1053 + reactTag:self.reactTag
  1054 + scrollViewContentOffset:scrollView.contentOffset
  1055 + scrollViewContentInset:scrollView.contentInset
  1056 + scrollViewContentSize:scrollView.contentSize
  1057 + scrollViewFrame:scrollView.frame
  1058 + scrollViewZoomScale:scrollView.zoomScale
  1059 + userData:userData
  1060 + coalescingKey:_coalescingKey];
  1061 + [_eventDispatcher sendEvent:scrollEvent];
  1062 +}
  1063 +
  1064 +@end
  1065 +
  1066 +@implementation RCTEventDispatcher (RCTMJScrollView)
  1067 +
  1068 +- (void)sendFakeScrollEvent:(NSNumber *)reactTag
  1069 +{
  1070 + // Use the selector here in case the onScroll block property is ever renamed
  1071 + NSString *eventName = NSStringFromSelector(@selector(onScroll));
  1072 + RCTMJScrollEvent *fakeScrollEvent = [[RCTMJScrollEvent alloc] initWithEventName:eventName
  1073 + reactTag:reactTag
  1074 + scrollViewContentOffset:CGPointZero
  1075 + scrollViewContentInset:UIEdgeInsetsZero
  1076 + scrollViewContentSize:CGSizeZero
  1077 + scrollViewFrame:CGRectZero
  1078 + scrollViewZoomScale:0
  1079 + userData:nil
  1080 + coalescingKey:0];
  1081 + [self sendEvent:fakeScrollEvent];
  1082 +}
  1083 +
  1084 +@end
  1085 +
  1 +#import <React/RCTConvert.h>
  2 +#import <React/RCTViewManager.h>
  3 +
  4 +@interface RCTConvert (UIScrollView)
  5 +
  6 ++ (UIScrollViewKeyboardDismissMode)UIScrollViewKeyboardDismissMode:(id)json;
  7 +
  8 +@end
  9 +
  10 +@interface RCTMJScrollViewManager : RCTViewManager
  11 +
  12 +@end
  1 +/**
  2 + * Copyright (c) 2015-present, Facebook, Inc.
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +
  8 +#import "RCTMJScrollViewManager.h"
  9 +
  10 +#import <React/RCTBridge.h>
  11 +#import "RCTMJScrollView.h"
  12 +#import <React/RCTShadowView.h>
  13 +#import <React/RCTUIManager.h>
  14 +
  15 +@interface RCTMJScrollView (Private1)
  16 +
  17 +- (NSArray<NSDictionary *> *)calculateChildFramesData;
  18 +
  19 +@end
  20 +
  21 +@implementation RCTConvert (UIScrollView)
  22 +
  23 +RCT_ENUM_CONVERTER(UIScrollViewKeyboardDismissMode, (@{
  24 + @"none": @(UIScrollViewKeyboardDismissModeNone),
  25 + @"on-drag": @(UIScrollViewKeyboardDismissModeOnDrag),
  26 + @"interactive": @(UIScrollViewKeyboardDismissModeInteractive),
  27 + // Backwards compatibility
  28 + @"onDrag": @(UIScrollViewKeyboardDismissModeOnDrag),
  29 + }), UIScrollViewKeyboardDismissModeNone, integerValue)
  30 +
  31 +RCT_ENUM_CONVERTER(UIScrollViewIndicatorStyle, (@{
  32 + @"default": @(UIScrollViewIndicatorStyleDefault),
  33 + @"black": @(UIScrollViewIndicatorStyleBlack),
  34 + @"white": @(UIScrollViewIndicatorStyleWhite),
  35 + }), UIScrollViewIndicatorStyleDefault, integerValue)
  36 +
  37 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
  38 +RCT_ENUM_CONVERTER(UIScrollViewContentInsetAdjustmentBehavior, (@{
  39 + @"automatic": @(UIScrollViewContentInsetAdjustmentAutomatic),
  40 + @"scrollableAxes": @(UIScrollViewContentInsetAdjustmentScrollableAxes),
  41 + @"never": @(UIScrollViewContentInsetAdjustmentNever),
  42 + @"always": @(UIScrollViewContentInsetAdjustmentAlways),
  43 + }), UIScrollViewContentInsetAdjustmentNever, integerValue)
  44 +#endif
  45 +
  46 +@end
  47 +
  48 +@implementation RCTMJScrollViewManager
  49 +
  50 +RCT_EXPORT_MODULE()
  51 +
  52 +- (UIView *)view
  53 +{
  54 + return [[RCTMJScrollView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
  55 +}
  56 +
  57 +RCT_EXPORT_VIEW_PROPERTY(alwaysBounceHorizontal, BOOL)
  58 +RCT_EXPORT_VIEW_PROPERTY(alwaysBounceVertical, BOOL)
  59 +RCT_EXPORT_VIEW_PROPERTY(bounces, BOOL)
  60 +RCT_EXPORT_VIEW_PROPERTY(bouncesZoom, BOOL)
  61 +RCT_EXPORT_VIEW_PROPERTY(canCancelContentTouches, BOOL)
  62 +RCT_EXPORT_VIEW_PROPERTY(centerContent, BOOL)
  63 +RCT_EXPORT_VIEW_PROPERTY(maintainVisibleContentPosition, NSDictionary)
  64 +RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
  65 +RCT_EXPORT_VIEW_PROPERTY(decelerationRate, CGFloat)
  66 +RCT_EXPORT_VIEW_PROPERTY(directionalLockEnabled, BOOL)
  67 +RCT_EXPORT_VIEW_PROPERTY(indicatorStyle, UIScrollViewIndicatorStyle)
  68 +RCT_EXPORT_VIEW_PROPERTY(keyboardDismissMode, UIScrollViewKeyboardDismissMode)
  69 +RCT_EXPORT_VIEW_PROPERTY(maximumZoomScale, CGFloat)
  70 +RCT_EXPORT_VIEW_PROPERTY(minimumZoomScale, CGFloat)
  71 +RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
  72 +#if !TARGET_OS_TV
  73 +RCT_EXPORT_VIEW_PROPERTY(pagingEnabled, BOOL)
  74 +RCT_REMAP_VIEW_PROPERTY(pinchGestureEnabled, scrollView.pinchGestureEnabled, BOOL)
  75 +RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL)
  76 +#endif
  77 +RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL)
  78 +RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL)
  79 +RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
  80 +RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
  81 +RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
  82 +RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
  83 +RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
  84 +RCT_EXPORT_VIEW_PROPERTY(snapToAlignment, NSString)
  85 +RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint)
  86 +RCT_EXPORT_VIEW_PROPERTY(onScrollBeginDrag, RCTDirectEventBlock)
  87 +RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
  88 +RCT_EXPORT_VIEW_PROPERTY(onScrollEndDrag, RCTDirectEventBlock)
  89 +RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollBegin, RCTDirectEventBlock)
  90 +RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock)
  91 +RCT_EXPORT_VIEW_PROPERTY(DEPRECATED_sendUpdatedChildFrames, BOOL)
  92 +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
  93 +RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
  94 +#endif
  95 +
  96 +// overflow is used both in css-layout as well as by react-native. In css-layout
  97 +// we always want to treat overflow as scroll but depending on what the overflow
  98 +// is set to from js we want to clip drawing or not. This piece of code ensures
  99 +// that css-layout is always treating the contents of a scroll container as
  100 +// overflow: 'scroll'.
  101 +RCT_CUSTOM_SHADOW_PROPERTY(overflow, YGOverflow, RCTShadowView) {
  102 +#pragma unused (json)
  103 + view.overflow = YGOverflowScroll;
  104 +}
  105 +
  106 +RCT_EXPORT_METHOD(getContentSize:(nonnull NSNumber *)reactTag
  107 + callback:(RCTResponseSenderBlock)callback)
  108 +{
  109 + [self.bridge.uiManager addUIBlock:
  110 + ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTMJScrollView *> *viewRegistry) {
  111 +
  112 + RCTMJScrollView *view = viewRegistry[reactTag];
  113 + if (!view || ![view isKindOfClass:[RCTMJScrollView class]]) {
  114 + RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
  115 + return;
  116 + }
  117 +
  118 + CGSize size = view.scrollView.contentSize;
  119 + callback(@[@{
  120 + @"width" : @(size.width),
  121 + @"height" : @(size.height)
  122 + }]);
  123 + }];
  124 +}
  125 +
  126 +RCT_EXPORT_METHOD(calculateChildFrames:(nonnull NSNumber *)reactTag
  127 + callback:(RCTResponseSenderBlock)callback)
  128 +{
  129 + [self.bridge.uiManager addUIBlock:
  130 + ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTMJScrollView *> *viewRegistry) {
  131 +
  132 + RCTMJScrollView *view = viewRegistry[reactTag];
  133 + if (!view || ![view isKindOfClass:[RCTMJScrollView class]]) {
  134 + RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
  135 + return;
  136 + }
  137 +
  138 + NSArray<NSDictionary *> *childFrames = [view calculateChildFramesData];
  139 + if (childFrames) {
  140 + callback(@[childFrames]);
  141 + }
  142 + }];
  143 +}
  144 +
  145 +RCT_EXPORT_METHOD(scrollTo:(nonnull NSNumber *)reactTag
  146 + offsetX:(CGFloat)x
  147 + offsetY:(CGFloat)y
  148 + animated:(BOOL)animated)
  149 +{
  150 + [self.bridge.uiManager addUIBlock:
  151 + ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
  152 + UIView *view = viewRegistry[reactTag];
  153 + if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
  154 + [(id<RCTScrollableProtocol>)view scrollToOffset:(CGPoint){x, y} animated:animated];
  155 + } else {
  156 + RCTLogError(@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
  157 + "with tag #%@", view, reactTag);
  158 + }
  159 + }];
  160 +}
  161 +
  162 +RCT_EXPORT_METHOD(scrollToEnd:(nonnull NSNumber *)reactTag
  163 + animated:(BOOL)animated)
  164 +{
  165 + [self.bridge.uiManager addUIBlock:
  166 + ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
  167 + UIView *view = viewRegistry[reactTag];
  168 + if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
  169 + [(id<RCTScrollableProtocol>)view scrollToEnd:animated];
  170 + } else {
  171 + RCTLogError(@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
  172 + "with tag #%@", view, reactTag);
  173 + }
  174 + }];
  175 +}
  176 +
  177 +RCT_EXPORT_METHOD(zoomToRect:(nonnull NSNumber *)reactTag
  178 + withRect:(CGRect)rect
  179 + animated:(BOOL)animated)
  180 +{
  181 + [self.bridge.uiManager addUIBlock:
  182 + ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
  183 + UIView *view = viewRegistry[reactTag];
  184 + if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
  185 + [(id<RCTScrollableProtocol>)view zoomToRect:rect animated:animated];
  186 + } else {
  187 + RCTLogError(@"tried to zoomToRect: on non-RCTScrollableProtocol view %@ "
  188 + "with tag #%@", view, reactTag);
  189 + }
  190 + }];
  191 +}
  192 +
  193 +RCT_EXPORT_METHOD(flashScrollIndicators:(nonnull NSNumber *)reactTag)
  194 +{
  195 + [self.bridge.uiManager addUIBlock:
  196 + ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTMJScrollView *> *viewRegistry){
  197 +
  198 + RCTMJScrollView *view = viewRegistry[reactTag];
  199 + if (!view || ![view isKindOfClass:[RCTMJScrollView class]]) {
  200 + RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
  201 + return;
  202 + }
  203 +
  204 + [view.scrollView flashScrollIndicators];
  205 + }];
  206 +}
  207 +
  208 +@end
  209 +
  1 +/**
  2 + * Copyright (c) 2015-present, Facebook, Inc.
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +
  8 +#import <UIKit/UIKit.h>
  9 +
  10 +@interface UIView (Private1)
  11 +
  12 +// remove clipped subviews implementation
  13 +- (void)react_remountAllSubviews;
  14 +- (void)react_updateClippedSubviewsWithClipRect:(CGRect)clipRect relativeToView:(UIView *)clipView;
  15 +- (UIView *)react_findClipView;
  16 +
  17 +@end
  18 +
  1 +
  2 +#if __has_include("RCTBridgeModule.h")
  3 +#import "RCTBridgeModule.h"
  4 +#else
  5 +#import <React/RCTBridgeModule.h>
  6 +#endif
  7 +
  8 +@interface RNVodeMjRefresh : NSObject <RCTBridgeModule>
  9 +
  10 +@end
  11 +
  1 +
  2 +#import "RNVodeMjRefresh.h"
  3 +
  4 +@implementation RNVodeMjRefresh
  5 +
  6 +- (dispatch_queue_t)methodQueue
  7 +{
  8 + return dispatch_get_main_queue();
  9 +}
  10 +RCT_EXPORT_MODULE()
  11 +
  12 +@end
  13 +
  1 +
  2 +Pod::Spec.new do |s|
  3 + s.name = "RNVodeMjRefresh"
  4 + s.version = "1.0.0"
  5 + s.summary = "RNVodeMjRefresh"
  6 + s.description = <<-DESC
  7 + RNVodeMjRefresh
  8 + DESC
  9 + s.homepage = ""
  10 + s.license = "MIT"
  11 + # s.license = { :type => "MIT", :file => "FILE_LICENSE" }
  12 + s.author = { "author" => "author@domain.cn" }
  13 + s.platform = :ios, "7.0"
  14 + s.source = { :git => "https://github.com/author/RNVodeMjRefresh.git", :tag => "master" }
  15 + s.source_files = "RNVodeMjRefresh/**/*.{h,m}"
  16 + s.requires_arc = true
  17 +
  18 +
  19 + s.dependency "React"
  20 + s.dependency 'MJRefresh', "~> 3.1.1"
  21 + #s.dependency "others"
  22 +
  23 +end
  24 +
  25 +
  1 +// !$*UTF8*$!
  2 +{
  3 + archiveVersion = 1;
  4 + classes = {
  5 + };
  6 + objectVersion = 46;
  7 + objects = {
  8 +
  9 +/* Begin PBXBuildFile section */
  10 + 842BE70425402C7F008D3A2E /* RCTMJScrollContentShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 842BE6FB25402C7F008D3A2E /* RCTMJScrollContentShadowView.m */; };
  11 + 842BE70525402C7F008D3A2E /* RCTMJRefreshHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 842BE6FC25402C7F008D3A2E /* RCTMJRefreshHeader.m */; };
  12 + 842BE70625402C7F008D3A2E /* RCTMJScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 842BE6FD25402C7F008D3A2E /* RCTMJScrollView.m */; };
  13 + 842BE70725402C7F008D3A2E /* RCTMJScrollViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 842BE6FE25402C7F008D3A2E /* RCTMJScrollViewManager.m */; };
  14 + 842BE70825402C7F008D3A2E /* RCTMJScrollContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 842BE70025402C7F008D3A2E /* RCTMJScrollContentView.m */; };
  15 + 842BE70925402C7F008D3A2E /* RCTMJRefreshViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 842BE70125402C7F008D3A2E /* RCTMJRefreshViewManager.m */; };
  16 + 842BE70A25402C7F008D3A2E /* RCTMJScrollContentViewMananger.m in Sources */ = {isa = PBXBuildFile; fileRef = 842BE70325402C7F008D3A2E /* RCTMJScrollContentViewMananger.m */; };
  17 + 842BE70C25402C95008D3A2E /* RCTMJRefreshHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 842BE6F725402C7F008D3A2E /* RCTMJRefreshHeader.h */; };
  18 + 842BE70D25402C95008D3A2E /* RCTMJScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = 842BE6F825402C7F008D3A2E /* RCTMJScrollView.h */; };
  19 + 842BE70E25402C95008D3A2E /* RCTMJScrollContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 842BE6F925402C7F008D3A2E /* RCTMJScrollContentView.h */; };
  20 + 842BE70F25402C95008D3A2E /* RCTMJScrollViewManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 842BE6FA25402C7F008D3A2E /* RCTMJScrollViewManager.h */; };
  21 + 842BE71025402C95008D3A2E /* UIView+Private1.h in Headers */ = {isa = PBXBuildFile; fileRef = 842BE6FF25402C7F008D3A2E /* UIView+Private1.h */; };
  22 + 842BE71125402C95008D3A2E /* RCTMJScrollContentShadowView.h in Headers */ = {isa = PBXBuildFile; fileRef = 842BE70225402C7F008D3A2E /* RCTMJScrollContentShadowView.h */; };
  23 + 842BE71225402C95008D3A2E /* RNVodeMjRefresh.h in Headers */ = {isa = PBXBuildFile; fileRef = B3E7B5881CC2AC0600A0062D /* RNVodeMjRefresh.h */; };
  24 + B3E7B58A1CC2AC0600A0062D /* RNVodeMjRefresh.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNVodeMjRefresh.m */; };
  25 +/* End PBXBuildFile section */
  26 +
  27 +/* Begin PBXCopyFilesBuildPhase section */
  28 + 58B511D91A9E6C8500147676 /* CopyFiles */ = {
  29 + isa = PBXCopyFilesBuildPhase;
  30 + buildActionMask = 2147483647;
  31 + dstPath = "include/$(PRODUCT_NAME)";
  32 + dstSubfolderSpec = 16;
  33 + files = (
  34 + );
  35 + runOnlyForDeploymentPostprocessing = 0;
  36 + };
  37 +/* End PBXCopyFilesBuildPhase section */
  38 +
  39 +/* Begin PBXFileReference section */
  40 + 134814201AA4EA6300B7C361 /* libRNVodeMjRefresh.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNVodeMjRefresh.a; sourceTree = BUILT_PRODUCTS_DIR; };
  41 + 842BE6F725402C7F008D3A2E /* RCTMJRefreshHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMJRefreshHeader.h; sourceTree = "<group>"; };
  42 + 842BE6F825402C7F008D3A2E /* RCTMJScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMJScrollView.h; sourceTree = "<group>"; };
  43 + 842BE6F925402C7F008D3A2E /* RCTMJScrollContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMJScrollContentView.h; sourceTree = "<group>"; };
  44 + 842BE6FA25402C7F008D3A2E /* RCTMJScrollViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMJScrollViewManager.h; sourceTree = "<group>"; };
  45 + 842BE6FB25402C7F008D3A2E /* RCTMJScrollContentShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMJScrollContentShadowView.m; sourceTree = "<group>"; };
  46 + 842BE6FC25402C7F008D3A2E /* RCTMJRefreshHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMJRefreshHeader.m; sourceTree = "<group>"; };
  47 + 842BE6FD25402C7F008D3A2E /* RCTMJScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMJScrollView.m; sourceTree = "<group>"; };
  48 + 842BE6FE25402C7F008D3A2E /* RCTMJScrollViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMJScrollViewManager.m; sourceTree = "<group>"; };
  49 + 842BE6FF25402C7F008D3A2E /* UIView+Private1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Private1.h"; sourceTree = "<group>"; };
  50 + 842BE70025402C7F008D3A2E /* RCTMJScrollContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMJScrollContentView.m; sourceTree = "<group>"; };
  51 + 842BE70125402C7F008D3A2E /* RCTMJRefreshViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMJRefreshViewManager.m; sourceTree = "<group>"; };
  52 + 842BE70225402C7F008D3A2E /* RCTMJScrollContentShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMJScrollContentShadowView.h; sourceTree = "<group>"; };
  53 + 842BE70325402C7F008D3A2E /* RCTMJScrollContentViewMananger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMJScrollContentViewMananger.m; sourceTree = "<group>"; };
  54 + B3E7B5881CC2AC0600A0062D /* RNVodeMjRefresh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNVodeMjRefresh.h; sourceTree = "<group>"; };
  55 + B3E7B5891CC2AC0600A0062D /* RNVodeMjRefresh.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNVodeMjRefresh.m; sourceTree = "<group>"; };
  56 +/* End PBXFileReference section */
  57 +
  58 +/* Begin PBXFrameworksBuildPhase section */
  59 + 58B511D81A9E6C8500147676 /* Frameworks */ = {
  60 + isa = PBXFrameworksBuildPhase;
  61 + buildActionMask = 2147483647;
  62 + files = (
  63 + );
  64 + runOnlyForDeploymentPostprocessing = 0;
  65 + };
  66 +/* End PBXFrameworksBuildPhase section */
  67 +
  68 +/* Begin PBXGroup section */
  69 + 134814211AA4EA7D00B7C361 /* Products */ = {
  70 + isa = PBXGroup;
  71 + children = (
  72 + 134814201AA4EA6300B7C361 /* libRNVodeMjRefresh.a */,
  73 + );
  74 + name = Products;
  75 + sourceTree = "<group>";
  76 + };
  77 + 58B511D21A9E6C8500147676 = {
  78 + isa = PBXGroup;
  79 + children = (
  80 + 842BE6F625402C7F008D3A2E /* RCTMJRefreshHeader */,
  81 + B3E7B5881CC2AC0600A0062D /* RNVodeMjRefresh.h */,
  82 + B3E7B5891CC2AC0600A0062D /* RNVodeMjRefresh.m */,
  83 + 134814211AA4EA7D00B7C361 /* Products */,
  84 + );
  85 + sourceTree = "<group>";
  86 + };
  87 + 842BE6F625402C7F008D3A2E /* RCTMJRefreshHeader */ = {
  88 + isa = PBXGroup;
  89 + children = (
  90 + 842BE6F725402C7F008D3A2E /* RCTMJRefreshHeader.h */,
  91 + 842BE6F825402C7F008D3A2E /* RCTMJScrollView.h */,
  92 + 842BE6F925402C7F008D3A2E /* RCTMJScrollContentView.h */,
  93 + 842BE6FA25402C7F008D3A2E /* RCTMJScrollViewManager.h */,
  94 + 842BE6FB25402C7F008D3A2E /* RCTMJScrollContentShadowView.m */,
  95 + 842BE6FC25402C7F008D3A2E /* RCTMJRefreshHeader.m */,
  96 + 842BE6FD25402C7F008D3A2E /* RCTMJScrollView.m */,
  97 + 842BE6FE25402C7F008D3A2E /* RCTMJScrollViewManager.m */,
  98 + 842BE6FF25402C7F008D3A2E /* UIView+Private1.h */,
  99 + 842BE70025402C7F008D3A2E /* RCTMJScrollContentView.m */,
  100 + 842BE70125402C7F008D3A2E /* RCTMJRefreshViewManager.m */,
  101 + 842BE70225402C7F008D3A2E /* RCTMJScrollContentShadowView.h */,
  102 + 842BE70325402C7F008D3A2E /* RCTMJScrollContentViewMananger.m */,
  103 + );
  104 + path = RCTMJRefreshHeader;
  105 + sourceTree = "<group>";
  106 + };
  107 +/* End PBXGroup section */
  108 +
  109 +/* Begin PBXHeadersBuildPhase section */
  110 + 842BE70B25402C86008D3A2E /* Headers */ = {
  111 + isa = PBXHeadersBuildPhase;
  112 + buildActionMask = 2147483647;
  113 + files = (
  114 + 842BE70C25402C95008D3A2E /* RCTMJRefreshHeader.h in Headers */,
  115 + 842BE70D25402C95008D3A2E /* RCTMJScrollView.h in Headers */,
  116 + 842BE70E25402C95008D3A2E /* RCTMJScrollContentView.h in Headers */,
  117 + 842BE70F25402C95008D3A2E /* RCTMJScrollViewManager.h in Headers */,
  118 + 842BE71025402C95008D3A2E /* UIView+Private1.h in Headers */,
  119 + 842BE71125402C95008D3A2E /* RCTMJScrollContentShadowView.h in Headers */,
  120 + 842BE71225402C95008D3A2E /* RNVodeMjRefresh.h in Headers */,
  121 + );
  122 + runOnlyForDeploymentPostprocessing = 0;
  123 + };
  124 +/* End PBXHeadersBuildPhase section */
  125 +
  126 +/* Begin PBXNativeTarget section */
  127 + 58B511DA1A9E6C8500147676 /* RNVodeMjRefresh */ = {
  128 + isa = PBXNativeTarget;
  129 + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNVodeMjRefresh" */;
  130 + buildPhases = (
  131 + 842BE70B25402C86008D3A2E /* Headers */,
  132 + 58B511D71A9E6C8500147676 /* Sources */,
  133 + 58B511D81A9E6C8500147676 /* Frameworks */,
  134 + 58B511D91A9E6C8500147676 /* CopyFiles */,
  135 + );
  136 + buildRules = (
  137 + );
  138 + dependencies = (
  139 + );
  140 + name = RNVodeMjRefresh;
  141 + productName = RCTDataManager;
  142 + productReference = 134814201AA4EA6300B7C361 /* libRNVodeMjRefresh.a */;
  143 + productType = "com.apple.product-type.library.static";
  144 + };
  145 +/* End PBXNativeTarget section */
  146 +
  147 +/* Begin PBXProject section */
  148 + 58B511D31A9E6C8500147676 /* Project object */ = {
  149 + isa = PBXProject;
  150 + attributes = {
  151 + LastUpgradeCheck = 0830;
  152 + ORGANIZATIONNAME = Facebook;
  153 + TargetAttributes = {
  154 + 58B511DA1A9E6C8500147676 = {
  155 + CreatedOnToolsVersion = 6.1.1;
  156 + };
  157 + };
  158 + };
  159 + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNVodeMjRefresh" */;
  160 + compatibilityVersion = "Xcode 3.2";
  161 + developmentRegion = English;
  162 + hasScannedForEncodings = 0;
  163 + knownRegions = (
  164 + English,
  165 + en,
  166 + );
  167 + mainGroup = 58B511D21A9E6C8500147676;
  168 + productRefGroup = 58B511D21A9E6C8500147676;
  169 + projectDirPath = "";
  170 + projectRoot = "";
  171 + targets = (
  172 + 58B511DA1A9E6C8500147676 /* RNVodeMjRefresh */,
  173 + );
  174 + };
  175 +/* End PBXProject section */
  176 +
  177 +/* Begin PBXSourcesBuildPhase section */
  178 + 58B511D71A9E6C8500147676 /* Sources */ = {
  179 + isa = PBXSourcesBuildPhase;
  180 + buildActionMask = 2147483647;
  181 + files = (
  182 + 842BE70525402C7F008D3A2E /* RCTMJRefreshHeader.m in Sources */,
  183 + 842BE70925402C7F008D3A2E /* RCTMJRefreshViewManager.m in Sources */,
  184 + 842BE70A25402C7F008D3A2E /* RCTMJScrollContentViewMananger.m in Sources */,
  185 + 842BE70425402C7F008D3A2E /* RCTMJScrollContentShadowView.m in Sources */,
  186 + 842BE70625402C7F008D3A2E /* RCTMJScrollView.m in Sources */,
  187 + B3E7B58A1CC2AC0600A0062D /* RNVodeMjRefresh.m in Sources */,
  188 + 842BE70825402C7F008D3A2E /* RCTMJScrollContentView.m in Sources */,
  189 + 842BE70725402C7F008D3A2E /* RCTMJScrollViewManager.m in Sources */,
  190 + );
  191 + runOnlyForDeploymentPostprocessing = 0;
  192 + };
  193 +/* End PBXSourcesBuildPhase section */
  194 +
  195 +/* Begin XCBuildConfiguration section */
  196 + 58B511ED1A9E6C8500147676 /* Debug */ = {
  197 + isa = XCBuildConfiguration;
  198 + buildSettings = {
  199 + ALWAYS_SEARCH_USER_PATHS = NO;
  200 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
  201 + CLANG_CXX_LIBRARY = "libc++";
  202 + CLANG_ENABLE_MODULES = YES;
  203 + CLANG_ENABLE_OBJC_ARC = YES;
  204 + CLANG_WARN_BOOL_CONVERSION = YES;
  205 + CLANG_WARN_CONSTANT_CONVERSION = YES;
  206 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
  207 + CLANG_WARN_EMPTY_BODY = YES;
  208 + CLANG_WARN_ENUM_CONVERSION = YES;
  209 + CLANG_WARN_INFINITE_RECURSION = YES;
  210 + CLANG_WARN_INT_CONVERSION = YES;
  211 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
  212 + CLANG_WARN_SUSPICIOUS_MOVE = YES;
  213 + CLANG_WARN_UNREACHABLE_CODE = YES;
  214 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
  215 + COPY_PHASE_STRIP = NO;
  216 + ENABLE_STRICT_OBJC_MSGSEND = YES;
  217 + ENABLE_TESTABILITY = YES;
  218 + GCC_C_LANGUAGE_STANDARD = gnu99;
  219 + GCC_DYNAMIC_NO_PIC = NO;
  220 + GCC_NO_COMMON_BLOCKS = YES;
  221 + GCC_OPTIMIZATION_LEVEL = 0;
  222 + GCC_PREPROCESSOR_DEFINITIONS = (
  223 + "DEBUG=1",
  224 + "$(inherited)",
  225 + );
  226 + GCC_SYMBOLS_PRIVATE_EXTERN = NO;
  227 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
  228 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
  229 + GCC_WARN_UNDECLARED_SELECTOR = YES;
  230 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
  231 + GCC_WARN_UNUSED_FUNCTION = YES;
  232 + GCC_WARN_UNUSED_VARIABLE = YES;
  233 + IPHONEOS_DEPLOYMENT_TARGET = 8.0;
  234 + MTL_ENABLE_DEBUG_INFO = YES;
  235 + ONLY_ACTIVE_ARCH = YES;
  236 + SDKROOT = iphoneos;
  237 + };
  238 + name = Debug;
  239 + };
  240 + 58B511EE1A9E6C8500147676 /* Release */ = {
  241 + isa = XCBuildConfiguration;
  242 + buildSettings = {
  243 + ALWAYS_SEARCH_USER_PATHS = NO;
  244 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
  245 + CLANG_CXX_LIBRARY = "libc++";
  246 + CLANG_ENABLE_MODULES = YES;
  247 + CLANG_ENABLE_OBJC_ARC = YES;
  248 + CLANG_WARN_BOOL_CONVERSION = YES;
  249 + CLANG_WARN_CONSTANT_CONVERSION = YES;
  250 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
  251 + CLANG_WARN_EMPTY_BODY = YES;
  252 + CLANG_WARN_ENUM_CONVERSION = YES;
  253 + CLANG_WARN_INFINITE_RECURSION = YES;
  254 + CLANG_WARN_INT_CONVERSION = YES;
  255 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
  256 + CLANG_WARN_SUSPICIOUS_MOVE = YES;
  257 + CLANG_WARN_UNREACHABLE_CODE = YES;
  258 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
  259 + COPY_PHASE_STRIP = YES;
  260 + ENABLE_NS_ASSERTIONS = NO;
  261 + ENABLE_STRICT_OBJC_MSGSEND = YES;
  262 + GCC_C_LANGUAGE_STANDARD = gnu99;
  263 + GCC_NO_COMMON_BLOCKS = YES;
  264 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
  265 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
  266 + GCC_WARN_UNDECLARED_SELECTOR = YES;
  267 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
  268 + GCC_WARN_UNUSED_FUNCTION = YES;
  269 + GCC_WARN_UNUSED_VARIABLE = YES;
  270 + IPHONEOS_DEPLOYMENT_TARGET = 8.0;
  271 + MTL_ENABLE_DEBUG_INFO = NO;
  272 + SDKROOT = iphoneos;
  273 + VALIDATE_PRODUCT = YES;
  274 + };
  275 + name = Release;
  276 + };
  277 + 58B511F01A9E6C8500147676 /* Debug */ = {
  278 + isa = XCBuildConfiguration;
  279 + buildSettings = {
  280 + HEADER_SEARCH_PATHS = (
  281 + "$(inherited)",
  282 + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
  283 + "$(SRCROOT)/../../../React/**",
  284 + "$(SRCROOT)/../../react-native/React/**",
  285 + );
  286 + LIBRARY_SEARCH_PATHS = "$(inherited)";
  287 + OTHER_LDFLAGS = "-ObjC";
  288 + PRODUCT_NAME = RNVodeMjRefresh;
  289 + SKIP_INSTALL = YES;
  290 + };
  291 + name = Debug;
  292 + };
  293 + 58B511F11A9E6C8500147676 /* Release */ = {
  294 + isa = XCBuildConfiguration;
  295 + buildSettings = {
  296 + HEADER_SEARCH_PATHS = (
  297 + "$(inherited)",
  298 + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
  299 + "$(SRCROOT)/../../../React/**",
  300 + "$(SRCROOT)/../../react-native/React/**",
  301 + );
  302 + LIBRARY_SEARCH_PATHS = "$(inherited)";
  303 + OTHER_LDFLAGS = "-ObjC";
  304 + PRODUCT_NAME = RNVodeMjRefresh;
  305 + SKIP_INSTALL = YES;
  306 + };
  307 + name = Release;
  308 + };
  309 +/* End XCBuildConfiguration section */
  310 +
  311 +/* Begin XCConfigurationList section */
  312 + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNVodeMjRefresh" */ = {
  313 + isa = XCConfigurationList;
  314 + buildConfigurations = (
  315 + 58B511ED1A9E6C8500147676 /* Debug */,
  316 + 58B511EE1A9E6C8500147676 /* Release */,
  317 + );
  318 + defaultConfigurationIsVisible = 0;
  319 + defaultConfigurationName = Release;
  320 + };
  321 + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNVodeMjRefresh" */ = {
  322 + isa = XCConfigurationList;
  323 + buildConfigurations = (
  324 + 58B511F01A9E6C8500147676 /* Debug */,
  325 + 58B511F11A9E6C8500147676 /* Release */,
  326 + );
  327 + defaultConfigurationIsVisible = 0;
  328 + defaultConfigurationName = Release;
  329 + };
  330 +/* End XCConfigurationList section */
  331 + };
  332 + rootObject = 58B511D31A9E6C8500147676 /* Project object */;
  333 +}
  1 +// !$*UTF8*$!
  2 +<?xml version="1.0" encoding="UTF-8"?>
  3 +<Workspace
  4 + version = "1.0">
  5 + <FileRef
  6 + location = "group:RNVodeMjRefresh.xcodeproj">
  7 + </FileRef>
  8 +</Workspace>
  9 +
  1 +
  2 +{
  3 + "name": "react-native-vode-mj-refresh",
  4 + "version": "1.0.0",
  5 + "description": "",
  6 + "main": "index.js",
  7 + "scripts": {
  8 + "test": "echo \"Error: no test specified\" && exit 1"
  9 + },
  10 + "keywords": [
  11 + "react-native"
  12 + ],
  13 + "author": "",
  14 + "license": "",
  15 + "peerDependencies": {
  16 + "react-native": "^0.41.2"
  17 + }
  18 +}