Toggle navigation
Toggle navigation
This project
Loading...
Sign in
mobile
/
react
·
Commits
Go to a project
GitLab
Go to group
Project
Activity
Files
Commits
Pipelines
0
Builds
0
Graphs
Milestones
Issues
0
Merge Requests
0
Members
Labels
Wiki
Forks
Network
Create a new issue
1
Download as
Email Patches
Plain Diff
Browse Files
Authored by
lixia.zhang
7 years ago
Commit
3f0cfa681d95e6573d7b7b56a44f7f3d89abda4f
1 parent
45548ecf
master
...
1.0.5
1.0.7
dev
dev_RN_v0.60.5
patch-1
patch-2
removeUIWebview
update
usewkwebview
1.2.0
1.1.1
1.1.0
1.0.9
1.0.8
1.0.7
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
这几个文件用原生
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1 additions
and
1905 deletions
React.podspec
React/Base/RCTAssert.h
React/Base/RCTAssert.m
React/Views/ScrollView/RCTScrollView.h
React/Views/ScrollView/RCTScrollView.m
React/Views/ScrollView/RCTScrollViewManager.h
React/Views/ScrollView/RCTScrollViewManager.m
React.podspec
View file @
3f0cfa6
...
...
@@ -32,11 +32,9 @@ Pod::Spec.new do |s|
s
.
pod_target_xcconfig
=
{
"CLANG_CXX_LANGUAGE_STANDARD"
=>
"c++14"
}
s
.
preserve_paths
=
"package.json"
,
"LICENSE"
,
"LICENSE-docs"
,
"PATENTS"
s
.
cocoapods_version
=
">= 1.2.0"
s
.
frameworks
=
"Foundation"
,
"UIKit"
s
.
subspec
"Core"
do
|
ss
|
ss
.
dependency
"Yoga"
,
"
#{
package
[
"version"
]
}
.React"
ss
.
dependency
"YH_Analytics"
,
"3.1.6"
ss
.
source_files
=
"React/**/*.{c,h,m,mm,S,cpp}"
ss
.
exclude_files
=
"**/__tests__/*"
,
"IntegrationTests/*"
,
...
...
@@ -55,7 +53,7 @@ Pod::Spec.new do |s|
ss
.
header_dir
=
"React"
ss
.
framework
=
"JavaScriptCore"
ss
.
libraries
=
"stdc++"
ss
.
pod_target_xcconfig
=
{
"HEADER_SEARCH_PATHS"
=>
"
\"
$(PODS_TARGET_SRCROOT)/ReactCommon
\"
\"
$(PODS_TARGET_SRCROOT)/YH_Analytics
\"
\"
${PODS_ROOT}/../YH_Mall/Controls/YH_ThemeManager.{h,m}
\"
\"
${PODS_ROOT}/../YH_Mall_iPhone/Views/Custom/YH_ThemeButton.{h,m}
\"
\"
${PODS_ROOT}/../YH_Mall/Definition/YH_EnumConstants.h
\"
\"
${PODS_ROOT}/../YH_Mall/Dependences/MJRefresh
\"
"
}
ss
.
pod_target_xcconfig
=
{
"HEADER_SEARCH_PATHS"
=>
"
\"
$(PODS_TARGET_SRCROOT)/ReactCommon
\"
"
}
end
s
.
subspec
"CxxBridge"
do
|
ss
|
...
...
React/Base/RCTAssert.h
deleted
100755 → 0
View file @
45548ec
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
#import <React/RCTDefines.h>
/*
* Defined in RCTUtils.m
*/
RCT_EXTERN
BOOL
RCTIsMainQueue
(
void
);
/**
* This is the main assert macro that you should use. Asserts should be compiled out
* in production builds. You can customize the assert behaviour by setting a custom
* assert handler through `RCTSetAssertFunction`.
*/
#ifndef NS_BLOCK_ASSERTIONS
#define RCTAssert(condition, ...) do { \
if ((condition) == 0) { \
_RCTAssertFormat(#condition, __FILE__, __LINE__, __func__, __VA_ARGS__); \
if (RCT_NSASSERT) { \
[[NSAssertionHandler currentHandler] handleFailureInFunction:@(__func__) \
file:@(__FILE__) lineNumber:__LINE__ description:__VA_ARGS__]; \
} \
} \
} while (false)
#else
#define RCTAssert(condition, ...) do {} while (false)
#endif
RCT_EXTERN
void
_RCTAssertFormat
(
const
char
*
,
const
char
*
,
int
,
const
char
*
,
NSString
*
,
...
)
NS_FORMAT_FUNCTION
(
5
,
6
);
/**
* Report a fatal condition when executing. These calls will _NOT_ be compiled out
* in production, and crash the app by default. You can customize the fatal behaviour
* by setting a custom fatal handler through `RCTSetFatalHandler`.
*/
RCT_EXTERN
void
RCTFatal
(
NSError
*
error
);
/**
* The default error domain to be used for React errors.
*/
RCT_EXTERN
NSString
*
const
RCTErrorDomain
;
/**
* JS Stack trace provided as part of an NSError's userInfo
*/
RCT_EXTERN
NSString
*
const
RCTJSStackTraceKey
;
/**
* Raw JS Stack trace string provided as part of an NSError's userInfo
*/
RCT_EXTERN
NSString
*
const
RCTJSRawStackTraceKey
;
/**
* Name of fatal exceptions generated by RCTFatal
*/
RCT_EXTERN
NSString
*
const
RCTFatalExceptionName
;
/**
* A block signature to be used for custom assertion handling.
*/
typedef
void
(
^
RCTAssertFunction
)(
NSString
*
condition
,
NSString
*
fileName
,
NSNumber
*
lineNumber
,
NSString
*
function
,
NSString
*
message
);
typedef
void
(
^
RCTFatalHandler
)(
NSError
*
error
);
/**
* Convenience macro for asserting that a parameter is non-nil/non-zero.
*/
#define RCTAssertParam(name) RCTAssert(name, @"'%s' is a required parameter", #name)
/**
* Convenience macro for asserting that we're running on main queue.
*/
#define RCTAssertMainQueue() RCTAssert(RCTIsMainQueue(), \
@"This function must be called on the main queue")
/**
* Convenience macro for asserting that we're running off the main queue.
*/
#define RCTAssertNotMainQueue() RCTAssert(!RCTIsMainQueue(), \
@"This function must not be called on the main queue")
/**
* These methods get and set the current assert function called by the RCTAssert
* macros. You can use these to replace the standard behavior with custom assert
* functionality.
*/
RCT_EXTERN
void
RCTSetAssertFunction
(
RCTAssertFunction
assertFunction
);
RCT_EXTERN
RCTAssertFunction
RCTGetAssertFunction
(
void
);
/**
* This appends additional code to the existing assert function, without
* replacing the existing functionality. Useful if you just want to forward
* assert info to an extra service without changing the default behavior.
*/
RCT_EXTERN
void
RCTAddAssertFunction
(
RCTAssertFunction
assertFunction
);
/**
* This method temporarily overrides the assert function while performing the
* specified block. This is useful for testing purposes (to detect if a given
* function asserts something) or to suppress or override assertions temporarily.
*/
RCT_EXTERN
void
RCTPerformBlockWithAssertFunction
(
void
(
^
block
)(
void
),
RCTAssertFunction
assertFunction
);
/**
These methods get and set the current fatal handler called by the RCTFatal method.
*/
RCT_EXTERN
void
RCTSetFatalHandler
(
RCTFatalHandler
fatalHandler
);
RCT_EXTERN
RCTFatalHandler
RCTGetFatalHandler
(
void
);
/**
* Get the current thread's name (or the current queue, if in debug mode)
*/
RCT_EXTERN
NSString
*
RCTCurrentThreadName
(
void
);
/**
* Helper to get generate exception message from NSError
*/
RCT_EXTERN
NSString
*
RCTFormatError
(
NSString
*
message
,
NSArray
<
NSDictionary
<
NSString
*
,
id
>
*>
*
stacktrace
,
NSUInteger
maxMessageLength
);
/**
* Convenience macro to assert which thread is currently running (DEBUG mode only)
*/
#if DEBUG
#define RCTAssertThread(thread, format...) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
RCTAssert( \
[(id)thread isKindOfClass:[NSString class]] ? \
[RCTCurrentThreadName() isEqualToString:(NSString *)thread] : \
[(id)thread isKindOfClass:[NSThread class]] ? \
[NSThread currentThread] == (NSThread *)thread : \
dispatch_get_current_queue() == (dispatch_queue_t)thread, \
format); \
_Pragma("clang diagnostic pop")
#else
#define RCTAssertThread(thread, format...) do { } while (0)
#endif
React/Base/RCTAssert.m
deleted
100755 → 0
View file @
45548ec
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTAssert.h"
#import "YH_Analytics.h"
#import <React/RCTLog.h>
NSString
*
const
RCTErrorDomain
=
@"RCTErrorDomain"
;
NSString
*
const
RCTJSStackTraceKey
=
@"RCTJSStackTraceKey"
;
NSString
*
const
RCTJSRawStackTraceKey
=
@"RCTJSRawStackTraceKey"
;
NSString
*
const
RCTFatalExceptionName
=
@"RCTFatalException"
;
static
NSString
*
const
RCTAssertFunctionStack
=
@"RCTAssertFunctionStack"
;
RCTAssertFunction
RCTCurrentAssertFunction
=
nil
;
RCTFatalHandler
RCTCurrentFatalHandler
=
nil
;
NSException
*
_RCTNotImplementedException
(
SEL
,
Class
);
NSException
*
_RCTNotImplementedException
(
SEL
cmd
,
Class
cls
)
{
NSString
*
msg
=
[
NSString
stringWithFormat
:
@"%s is not implemented "
"for the class %@"
,
sel_getName
(
cmd
),
cls
];
return
[
NSException
exceptionWithName
:
@"RCTNotDesignatedInitializerException"
reason
:
msg
userInfo
:
nil
];
}
void
RCTSetAssertFunction
(
RCTAssertFunction
assertFunction
)
{
RCTCurrentAssertFunction
=
assertFunction
;
}
RCTAssertFunction
RCTGetAssertFunction
(
void
)
{
return
RCTCurrentAssertFunction
;
}
void
RCTAddAssertFunction
(
RCTAssertFunction
assertFunction
)
{
RCTAssertFunction
existing
=
RCTCurrentAssertFunction
;
if
(
existing
)
{
RCTCurrentAssertFunction
=
^
(
NSString
*
condition
,
NSString
*
fileName
,
NSNumber
*
lineNumber
,
NSString
*
function
,
NSString
*
message
)
{
existing
(
condition
,
fileName
,
lineNumber
,
function
,
message
);
assertFunction
(
condition
,
fileName
,
lineNumber
,
function
,
message
);
};
}
else
{
RCTCurrentAssertFunction
=
assertFunction
;
}
}
/**
* returns the topmost stacked assert function for the current thread, which
* may not be the same as the current value of RCTCurrentAssertFunction.
*/
static
RCTAssertFunction
RCTGetLocalAssertFunction
()
{
NSMutableDictionary
*
threadDictionary
=
[
NSThread
currentThread
].
threadDictionary
;
NSArray
<
RCTAssertFunction
>
*
functionStack
=
threadDictionary
[
RCTAssertFunctionStack
];
RCTAssertFunction
assertFunction
=
functionStack
.
lastObject
;
if
(
assertFunction
)
{
return
assertFunction
;
}
return
RCTCurrentAssertFunction
;
}
void
RCTPerformBlockWithAssertFunction
(
void
(
^
block
)(
void
),
RCTAssertFunction
assertFunction
)
{
NSMutableDictionary
*
threadDictionary
=
[
NSThread
currentThread
].
threadDictionary
;
NSMutableArray
<
RCTAssertFunction
>
*
functionStack
=
threadDictionary
[
RCTAssertFunctionStack
];
if
(
!
functionStack
)
{
functionStack
=
[
NSMutableArray
new
];
threadDictionary
[
RCTAssertFunctionStack
]
=
functionStack
;
}
[
functionStack
addObject
:
assertFunction
];
block
();
[
functionStack
removeLastObject
];
}
NSString
*
RCTCurrentThreadName
(
void
)
{
NSThread
*
thread
=
[
NSThread
currentThread
];
NSString
*
threadName
=
RCTIsMainQueue
()
||
thread
.
isMainThread
?
@"main"
:
thread
.
name
;
if
(
threadName
.
length
==
0
)
{
const
char
*
label
=
dispatch_queue_get_label
(
DISPATCH_CURRENT_QUEUE_LABEL
);
if
(
label
&&
strlen
(
label
)
>
0
)
{
threadName
=
@
(
label
);
}
else
{
threadName
=
[
NSString
stringWithFormat
:
@"%p"
,
thread
];
}
}
return
threadName
;
}
void
_RCTAssertFormat
(
const
char
*
condition
,
const
char
*
fileName
,
int
lineNumber
,
const
char
*
function
,
NSString
*
format
,
...)
{
RCTAssertFunction
assertFunction
=
RCTGetLocalAssertFunction
();
if
(
assertFunction
)
{
va_list
args
;
va_start
(
args
,
format
);
NSString
*
message
=
[[
NSString
alloc
]
initWithFormat
:
format
arguments
:
args
];
va_end
(
args
);
assertFunction
(
@
(
condition
),
@
(
fileName
),
@
(
lineNumber
),
@
(
function
),
message
);
}
}
void
RCTFatal
(
NSError
*
error
)
{
_RCTLogNativeInternal
(
RCTLogLevelFatal
,
NULL
,
0
,
@"%@"
,
error
.
localizedDescription
);
RCTFatalHandler
fatalHandler
=
RCTGetFatalHandler
();
if
(
fatalHandler
)
{
fatalHandler
(
error
);
}
else
{
#if DEBUG
@try
{
#endif
NSString
*
name
=
[
NSString
stringWithFormat
:
@"%@: %@"
,
RCTFatalExceptionName
,
error
.
localizedDescription
];
NSString
*
message
=
RCTFormatError
(
error
.
localizedDescription
,
error
.
userInfo
[
RCTJSStackTraceKey
],
75
);
#ifdef APPYOHO
NSArray
*
callStack
=
[
error
.
userInfo
objectForKey
:
RCTJSStackTraceKey
];
NSDictionary
*
parameters
=
@
{
JsonKeyCrashCallstack
:
callStack
?
:
@[],
JsonKeyCrashExceptionName
:
name
?
:
@""
,
JsonKeyCrashExceptionReason
:
message
?
:
@""
,
};
[[
YH_Analytics
sharedInstance
]
logError
:
kErrorTypeRNException
parameters
:
@
{
JsonKeyErrorST
:
parameters
}];
#endif
@throw
[[
NSException
alloc
]
initWithName
:
name
reason
:
message
userInfo
:
nil
];
#if DEBUG
}
@catch
(
NSException
*
e
)
{}
#endif
}
}
void
RCTSetFatalHandler
(
RCTFatalHandler
fatalhandler
)
{
RCTCurrentFatalHandler
=
fatalhandler
;
}
RCTFatalHandler
RCTGetFatalHandler
(
void
)
{
return
RCTCurrentFatalHandler
;
}
NSString
*
RCTFormatError
(
NSString
*
message
,
NSArray
<
NSDictionary
<
NSString
*
,
id
>
*>
*
stackTrace
,
NSUInteger
maxMessageLength
)
{
if
(
maxMessageLength
>
0
&&
message
.
length
>
maxMessageLength
)
{
message
=
[[
message
substringToIndex
:
maxMessageLength
]
stringByAppendingString
:
@"..."
];
}
NSMutableString
*
prettyStack
=
[
NSMutableString
string
];
if
(
stackTrace
)
{
[
prettyStack
appendString
:
@", stack:
\n
"
];
NSRegularExpression
*
regex
=
[
NSRegularExpression
regularExpressionWithPattern
:
@"^(
\\
d+
\\
.js)$"
options
:
NSRegularExpressionCaseInsensitive
error
:
NULL
];
for
(
NSDictionary
<
NSString
*
,
id
>
*
frame
in
stackTrace
)
{
NSString
*
fileName
=
[
frame
[
@"file"
]
lastPathComponent
];
if
(
fileName
&&
[
regex
numberOfMatchesInString
:
fileName
options
:
0
range
:
NSMakeRange
(
0
,
[
fileName
length
])])
{
fileName
=
[
fileName
stringByAppendingString
:
@":"
];
}
else
{
fileName
=
@""
;
}
[
prettyStack
appendFormat
:
@"%@@%@%@:%@
\n
"
,
frame
[
@"methodName"
],
fileName
,
frame
[
@"lineNumber"
],
frame
[
@"column"
]];
}
}
return
[
NSString
stringWithFormat
:
@"%@%@"
,
message
,
prettyStack
];
}
React/Views/ScrollView/RCTScrollView.h
deleted
100755 → 0
View file @
45548ec
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIScrollView.h>
#import <React/RCTAutoInsetsProtocol.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTScrollableProtocol.h>
#import <React/RCTView.h>
#import "MJRefresh.h"
#import "UIScrollView+MJRefresh.h"
@protocol
UIScrollViewDelegate
;
@interface
RCTScrollView
:
RCTView
<
UIScrollViewDelegate
,
RCTScrollableProtocol
,
RCTAutoInsetsProtocol
>
-
(
instancetype
)
initWithEventDispatcher
:
(
RCTEventDispatcher
*
)
eventDispatcher
NS_DESIGNATED_INITIALIZER
;
/**
* The `RCTScrollView` may have at most one single subview. This will ensure
* that the scroll view's `contentSize` will be efficiently set to the size of
* the single subview's frame. That frame size will be determined somewhat
* efficiently since it will have already been computed by the off-main-thread
* layout system.
*/
@property
(
nonatomic
,
readonly
)
UIView
*
contentView
;
/**
* If the `contentSize` is not specified (or is specified as {0, 0}, then the
* `contentSize` will automatically be determined by the size of the subview.
*/
@property
(
nonatomic
,
assign
)
CGSize
contentSize
;
/**
* The underlying scrollView (TODO: can we remove this?)
*/
@property
(
nonatomic
,
readonly
)
UIScrollView
*
scrollView
;
@property
(
nonatomic
,
assign
)
UIEdgeInsets
contentInset
;
@property
(
nonatomic
,
assign
)
BOOL
automaticallyAdjustContentInsets
;
@property
(
nonatomic
,
assign
)
BOOL
DEPRECATED_sendUpdatedChildFrames
;
@property
(
nonatomic
,
assign
)
NSTimeInterval
scrollEventThrottle
;
@property
(
nonatomic
,
assign
)
BOOL
centerContent
;
@property
(
nonatomic
,
copy
)
NSDictionary
*
maintainVisibleContentPosition
;
@property
(
nonatomic
,
assign
)
int
snapToInterval
;
@property
(
nonatomic
,
copy
)
NSString
*
snapToAlignment
;
// NOTE: currently these event props are only declared so we can export the
// event names to JS - we don't call the blocks directly because scroll events
// need to be coalesced before sending, for performance reasons.
@property
(
nonatomic
,
copy
)
RCTDirectEventBlock
onScrollBeginDrag
;
@property
(
nonatomic
,
copy
)
RCTDirectEventBlock
onScroll
;
@property
(
nonatomic
,
copy
)
RCTDirectEventBlock
onScrollEndDrag
;
@property
(
nonatomic
,
copy
)
RCTDirectEventBlock
onMomentumScrollBegin
;
@property
(
nonatomic
,
copy
)
RCTDirectEventBlock
onMomentumScrollEnd
;
// ********************
// add pull to refresh
// ********************
@property
(
assign
,
nonatomic
)
BOOL
currentRefreshingState
;
@property
(
nonatomic
,
copy
)
RCTDirectEventBlock
onRefreshData
;
@property
(
nonatomic
,
assign
)
BOOL
isOnPullToRefresh
;
@property
(
nonatomic
,
assign
)
BOOL
enablePullToRefresh
;
-
(
void
)
startPullToRefresh
;
-
(
void
)
stopPullToRefresh
;
// ********************
// add pull to refresh
// ********************
// ********************
// add 下拉刷新结束
// ********************
@property
(
nonatomic
,
copy
)
RCTDirectEventBlock
onFinishRefreshData
;
@end
@interface
RCTScrollView
(
Internal
)
-
(
void
)
updateContentOffsetIfNeeded
;
@end
@interface
RCTEventDispatcher
(
RCTScrollView
)
/**
* Send a fake scroll event.
*/
-
(
void
)
sendFakeScrollEvent
:
(
NSNumber
*
)
reactTag
;
@end
React/Views/ScrollView/RCTScrollView.m
deleted
100755 → 0
View file @
45548ec
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTScrollView.h"
#import <UIKit/UIKit.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTLog.h>
#import <React/RCTRefreshControl.h>
#import <React/RCTUIManager.h>
#import <React/RCTUtils.h>
#import <React/UIView+Private.h>
#import <React/UIView+React.h>
#import <React/RCTUIManagerObserverCoordinator.h>
#import <React/RCTUIManagerUtils.h>
@interface
RCTScrollEvent
:
NSObject
<
RCTEvent
>
-
(
instancetype
)
initWithEventName
:
(
NSString
*
)
eventName
reactTag
:
(
NSNumber
*
)
reactTag
scrollViewContentOffset
:
(
CGPoint
)
scrollViewContentOffset
scrollViewContentInset
:
(
UIEdgeInsets
)
scrollViewContentInset
scrollViewContentSize
:
(
CGSize
)
scrollViewContentSize
scrollViewFrame
:
(
CGRect
)
scrollViewFrame
scrollViewZoomScale
:
(
CGFloat
)
scrollViewZoomScale
userData
:
(
NSDictionary
*
)
userData
coalescingKey
:
(
uint16_t
)
coalescingKey
NS_DESIGNATED_INITIALIZER
;
@end
@implementation
RCTScrollEvent
{
CGPoint
_scrollViewContentOffset
;
UIEdgeInsets
_scrollViewContentInset
;
CGSize
_scrollViewContentSize
;
CGRect
_scrollViewFrame
;
CGFloat
_scrollViewZoomScale
;
NSDictionary
*
_userData
;
uint16_t
_coalescingKey
;
}
@synthesize
viewTag
=
_viewTag
;
@synthesize
eventName
=
_eventName
;
-
(
instancetype
)
initWithEventName
:(
NSString
*
)
eventName
reactTag
:(
NSNumber
*
)
reactTag
scrollViewContentOffset
:(
CGPoint
)
scrollViewContentOffset
scrollViewContentInset
:(
UIEdgeInsets
)
scrollViewContentInset
scrollViewContentSize
:(
CGSize
)
scrollViewContentSize
scrollViewFrame
:(
CGRect
)
scrollViewFrame
scrollViewZoomScale
:(
CGFloat
)
scrollViewZoomScale
userData
:(
NSDictionary
*
)
userData
coalescingKey
:(
uint16_t
)
coalescingKey
{
RCTAssertParam
(
reactTag
);
if
((
self
=
[
super
init
]))
{
_eventName
=
[
eventName
copy
];
_viewTag
=
reactTag
;
_scrollViewContentOffset
=
scrollViewContentOffset
;
_scrollViewContentInset
=
scrollViewContentInset
;
_scrollViewContentSize
=
scrollViewContentSize
;
_scrollViewFrame
=
scrollViewFrame
;
_scrollViewZoomScale
=
scrollViewZoomScale
;
_userData
=
userData
;
_coalescingKey
=
coalescingKey
;
}
return
self
;
}
RCT_NOT_IMPLEMENTED
(
-
(
instancetype
)
init
)
-
(
uint16_t
)
coalescingKey
{
return
_coalescingKey
;
}
-
(
NSDictionary
*
)
body
{
NSDictionary
*
body
=
@
{
@"contentOffset"
:
@
{
@"x"
:
@
(
_scrollViewContentOffset
.
x
),
@"y"
:
@
(
_scrollViewContentOffset
.
y
)
},
@"contentInset"
:
@
{
@"top"
:
@
(
_scrollViewContentInset
.
top
),
@"left"
:
@
(
_scrollViewContentInset
.
left
),
@"bottom"
:
@
(
_scrollViewContentInset
.
bottom
),
@"right"
:
@
(
_scrollViewContentInset
.
right
)
},
@"contentSize"
:
@
{
@"width"
:
@
(
_scrollViewContentSize
.
width
),
@"height"
:
@
(
_scrollViewContentSize
.
height
)
},
@"layoutMeasurement"
:
@
{
@"width"
:
@
(
_scrollViewFrame
.
size
.
width
),
@"height"
:
@
(
_scrollViewFrame
.
size
.
height
)
},
@"zoomScale"
:
@
(
_scrollViewZoomScale
?:
1
),
};
if
(
_userData
)
{
NSMutableDictionary
*
mutableBody
=
[
body
mutableCopy
];
[
mutableBody
addEntriesFromDictionary
:
_userData
];
body
=
mutableBody
;
}
return
body
;
}
-
(
BOOL
)
canCoalesce
{
return
YES
;
}
-
(
RCTScrollEvent
*
)
coalesceWithEvent
:
(
RCTScrollEvent
*
)
newEvent
{
NSArray
<
NSDictionary
*>
*
updatedChildFrames
=
[
_userData
[
@"updatedChildFrames"
]
arrayByAddingObjectsFromArray
:
newEvent
->
_userData
[
@"updatedChildFrames"
]];
if
(
updatedChildFrames
)
{
NSMutableDictionary
*
userData
=
[
newEvent
->
_userData
mutableCopy
];
userData
[
@"updatedChildFrames"
]
=
updatedChildFrames
;
newEvent
->
_userData
=
userData
;
}
return
newEvent
;
}
+
(
NSString
*
)
moduleDotMethod
{
return
@"RCTEventEmitter.receiveEvent"
;
}
-
(
NSArray
*
)
arguments
{
return
@[
self
.
viewTag
,
RCTNormalizeInputEventName
(
self
.
eventName
),
[
self
body
]];
}
@end
/**
* Include a custom scroll view subclass because we want to limit certain
* default UIKit behaviors such as textFields automatically scrolling
* scroll views that contain them.
*/
@interface
RCTCustomScrollView
:
UIScrollView
<
UIGestureRecognizerDelegate
>
@property
(
nonatomic
,
assign
)
BOOL
centerContent
;
@property
(
nonatomic
,
strong
)
RCTRefreshControl
*
rctRefreshControl
;
@property
(
nonatomic
,
assign
)
BOOL
pinchGestureEnabled
;
@end
@implementation
RCTCustomScrollView
{
__weak
UIView
*
_dockedHeaderView
;
}
-
(
instancetype
)
initWithFrame
:
(
CGRect
)
frame
{
if
((
self
=
[
super
initWithFrame
:
frame
]))
{
[
self
.
panGestureRecognizer
addTarget
:
self
action
:
@selector
(
handleCustomPan
:)];
if
([
self
respondsToSelector
:
@selector
(
setSemanticContentAttribute
:)])
{
// We intentionaly force `UIScrollView`s `semanticContentAttribute` to `LTR` here
// because this attribute affects a position of vertical scrollbar; we don't want this
// scrollbar flip because we also flip it with whole `UIScrollView` flip.
self
.
semanticContentAttribute
=
UISemanticContentAttributeForceLeftToRight
;
}
#if !TARGET_OS_TV
_pinchGestureEnabled
=
YES
;
#endif
}
return
self
;
}
-
(
UIView
*
)
contentView
{
return
((
RCTScrollView
*
)
self
.
superview
).
contentView
;
}
/**
* @return Whether or not the scroll view interaction should be blocked because
* JS was found to be the responder.
*/
-
(
BOOL
)
_shouldDisableScrollInteraction
{
// Since this may be called on every pan, we need to make sure to only climb
// the hierarchy on rare occasions.
UIView
*
JSResponder
=
[
RCTUIManager
JSResponder
];
if
(
JSResponder
&&
JSResponder
!=
self
.
superview
)
{
BOOL
superviewHasResponder
=
[
self
isDescendantOfView
:
JSResponder
];
return
superviewHasResponder
;
}
return
NO
;
}
-
(
void
)
handleCustomPan
:
(
__unused
UIPanGestureRecognizer
*
)
sender
{
if
([
self
_shouldDisableScrollInteraction
]
&&
!
[[
RCTUIManager
JSResponder
]
isKindOfClass
:[
RCTScrollView
class
]])
{
self
.
panGestureRecognizer
.
enabled
=
NO
;
self
.
panGestureRecognizer
.
enabled
=
YES
;
// TODO: If mid bounce, animate the scroll view to a non-bounced position
// while disabling (but only if `stopScrollInteractionIfJSHasResponder` was
// called *during* a `pan`). Currently, it will just snap into place which
// is not so bad either.
// Another approach:
// self.scrollEnabled = NO;
// self.scrollEnabled = YES;
}
}
-
(
void
)
scrollRectToVisible
:
(
CGRect
)
rect
animated
:
(
BOOL
)
animated
{
// Limiting scroll area to an area where we actually have content.
CGSize
contentSize
=
self
.
contentSize
;
UIEdgeInsets
contentInset
=
self
.
contentInset
;
CGSize
fullSize
=
CGSizeMake
(
contentSize
.
width
+
contentInset
.
left
+
contentInset
.
right
,
contentSize
.
height
+
contentInset
.
top
+
contentInset
.
bottom
);
rect
=
CGRectIntersection
((
CGRect
){
CGPointZero
,
fullSize
},
rect
);
if
(
CGRectIsNull
(
rect
))
{
return
;
}
[
super
scrollRectToVisible
:
rect
animated
:
animated
];
}
/**
* Returning `YES` cancels touches for the "inner" `view` and causes a scroll.
* Returning `NO` causes touches to be directed to that inner view and prevents
* the scroll view from scrolling.
*
* `YES` -> Allows scrolling.
* `NO` -> Doesn't allow scrolling.
*
* By default this returns NO for all views that are UIControls and YES for
* everything else. What that does is allows scroll views to scroll even when a
* touch started inside of a `UIControl` (`UIButton` etc). For React scroll
* views, we want the default to be the same behavior as `UIControl`s so we
* return `YES` by default. But there's one case where we want to block the
* scrolling no matter what: When JS believes it has its own responder lock on
* a view that is *above* the scroll view in the hierarchy. So we abuse this
* `touchesShouldCancelInContentView` API in order to stop the scroll view from
* scrolling in this case.
*
* We are not aware of *any* other solution to the problem because alternative
* approaches require that we disable the scrollview *before* touches begin or
* move. This approach (`touchesShouldCancelInContentView`) works even if the
* JS responder is set after touches start/move because
* `touchesShouldCancelInContentView` is called as soon as the scroll view has
* been touched and dragged *just* far enough to decide to begin the "drag"
* movement of the scroll interaction. Returning `NO`, will cause the drag
* operation to fail.
*
* `touchesShouldCancelInContentView` will stop the *initialization* of a
* scroll pan gesture and most of the time this is sufficient. On rare
* occasion, the scroll gesture would have already initialized right before JS
* notifies native of the JS responder being set. In order to recover from that
* timing issue we have a fallback that kills any ongoing pan gesture that
* occurs when native is notified of a JS responder.
*
* Note: Explicitly returning `YES`, instead of relying on the default fixes
* (at least) one bug where if you have a UIControl inside a UIScrollView and
* tap on the UIControl and then start dragging (to scroll), it won't scroll.
* Chat with @andras for more details.
*
* In order to have this called, you must have delaysContentTouches set to NO
* (which is the not the `UIKit` default).
*/
-
(
BOOL
)
touchesShouldCancelInContentView
:
(
__unused
UIView
*
)
view
{
//TODO: shouldn't this call super if _shouldDisableScrollInteraction returns NO?
return
!
[
self
_shouldDisableScrollInteraction
];
}
/*
* Automatically centers the content such that if the content is smaller than the
* ScrollView, we force it to be centered, but when you zoom or the content otherwise
* becomes larger than the ScrollView, there is no padding around the content but it
* can still fill the whole view.
*/
-
(
void
)
setContentOffset
:
(
CGPoint
)
contentOffset
{
UIView
*
contentView
=
[
self
contentView
];
if
(
contentView
&&
_centerContent
)
{
CGSize
subviewSize
=
contentView
.
frame
.
size
;
CGSize
scrollViewSize
=
self
.
bounds
.
size
;
if
(
subviewSize
.
width
<=
scrollViewSize
.
width
)
{
contentOffset
.
x
=
-
(
scrollViewSize
.
width
-
subviewSize
.
width
)
/
2
.
0
;
}
if
(
subviewSize
.
height
<=
scrollViewSize
.
height
)
{
contentOffset
.
y
=
-
(
scrollViewSize
.
height
-
subviewSize
.
height
)
/
2
.
0
;
}
}
super
.
contentOffset
=
contentOffset
;
}
-
(
void
)
setFrame
:
(
CGRect
)
frame
{
// Preserving and revalidating `contentOffset`.
CGPoint
originalOffset
=
self
.
contentOffset
;
[
super
setFrame
:
frame
];
UIEdgeInsets
contentInset
=
self
.
contentInset
;
CGSize
contentSize
=
self
.
contentSize
;
// If contentSize has not been measured yet we can't check bounds.
if
(
CGSizeEqualToSize
(
contentSize
,
CGSizeZero
))
{
self
.
contentOffset
=
originalOffset
;
}
else
{
// Make sure offset don't exceed bounds. This could happen on screen rotation.
CGSize
boundsSize
=
self
.
bounds
.
size
;
self
.
contentOffset
=
CGPointMake
(
MAX
(
-
contentInset
.
left
,
MIN
(
contentSize
.
width
-
boundsSize
.
width
+
contentInset
.
right
,
originalOffset
.
x
)),
MAX
(
-
contentInset
.
top
,
MIN
(
contentSize
.
height
-
boundsSize
.
height
+
contentInset
.
bottom
,
originalOffset
.
y
)));
}
}
-
(
void
)
setRctRefreshControl
:
(
RCTRefreshControl
*
)
refreshControl
{
if
(
_rctRefreshControl
)
{
[
_rctRefreshControl
removeFromSuperview
];
}
_rctRefreshControl
=
refreshControl
;
[
self
addSubview
:
_rctRefreshControl
];
}
-
(
void
)
setPinchGestureEnabled
:
(
BOOL
)
pinchGestureEnabled
{
self
.
pinchGestureRecognizer
.
enabled
=
pinchGestureEnabled
;
_pinchGestureEnabled
=
pinchGestureEnabled
;
}
-
(
void
)
didMoveToWindow
{
[
super
didMoveToWindow
];
// ScrollView enables pinch gesture late in its lifecycle. So simply setting it
// in the setter gets overriden when the view loads.
self
.
pinchGestureRecognizer
.
enabled
=
_pinchGestureEnabled
;
}
@end
@interface
RCTScrollView
()
<
RCTUIManagerObserver
>
@end
@implementation
RCTScrollView
{
RCTEventDispatcher
*
_eventDispatcher
;
CGRect
_prevFirstVisibleFrame
;
__weak
UIView
*
_firstVisibleView
;
RCTCustomScrollView
*
_scrollView
;
UIView
*
_contentView
;
NSTimeInterval
_lastScrollDispatchTime
;
NSMutableArray
<
NSValue
*>
*
_cachedChildFrames
;
BOOL
_allowNextScrollNoMatterWhat
;
CGRect
_lastClippedToRect
;
uint16_t
_coalescingKey
;
NSString
*
_lastEmittedEventName
;
NSHashTable
*
_scrollListeners
;
}
-
(
instancetype
)
initWithEventDispatcher
:
(
RCTEventDispatcher
*
)
eventDispatcher
{
RCTAssertParam
(
eventDispatcher
);
if
((
self
=
[
super
initWithFrame
:
CGRectZero
]))
{
_eventDispatcher
=
eventDispatcher
;
_scrollView
=
[[
RCTCustomScrollView
alloc
]
initWithFrame
:
CGRectZero
];
_scrollView
.
autoresizingMask
=
UIViewAutoresizingFlexibleWidth
|
UIViewAutoresizingFlexibleHeight
;
_scrollView
.
delegate
=
self
;
_scrollView
.
delaysContentTouches
=
NO
;
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
/* __IPHONE_11_0 */
// `contentInsetAdjustmentBehavior` is only available since iOS 11.
// We set the default behavior to "never" so that iOS
// doesn't do weird things to UIScrollView insets automatically
// and keeps it as an opt-in behavior.
if
([
_scrollView
respondsToSelector
:
@selector
(
setContentInsetAdjustmentBehavior
:)])
{
_scrollView
.
contentInsetAdjustmentBehavior
=
UIScrollViewContentInsetAdjustmentNever
;
}
#endif
_automaticallyAdjustContentInsets
=
YES
;
_DEPRECATED_sendUpdatedChildFrames
=
NO
;
_contentInset
=
UIEdgeInsetsZero
;
_contentSize
=
CGSizeZero
;
_lastClippedToRect
=
CGRectNull
;
_scrollEventThrottle
=
0
.
0
;
_lastScrollDispatchTime
=
0
;
_cachedChildFrames
=
[
NSMutableArray
new
];
_scrollListeners
=
[
NSHashTable
weakObjectsHashTable
];
[
self
addSubview
:
_scrollView
];
// ********************
// add pull to refresh
// ********************
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@selector
(
themeSwitchNotificationHandle
:)
name
:
YHBThemeDidChangeNotification
object
:
nil
];
}
return
self
;
}
RCT_NOT_IMPLEMENTED
(
-
(
instancetype
)
initWithFrame
:
(
CGRect
)
frame
)
RCT_NOT_IMPLEMENTED
(
-
(
instancetype
)
initWithCoder
:
(
NSCoder
*
)
aDecoder
)
static
inline
void
RCTApplyTransformationAccordingLayoutDirection
(
UIView
*
view
,
UIUserInterfaceLayoutDirection
layoutDirection
)
{
view
.
transform
=
layoutDirection
==
UIUserInterfaceLayoutDirectionLeftToRight
?
CGAffineTransformIdentity
:
CGAffineTransformMakeScale
(
-
1
,
1
);
}
-
(
void
)
setReactLayoutDirection
:
(
UIUserInterfaceLayoutDirection
)
layoutDirection
{
[
super
setReactLayoutDirection
:
layoutDirection
];
RCTApplyTransformationAccordingLayoutDirection
(
_scrollView
,
layoutDirection
);
RCTApplyTransformationAccordingLayoutDirection
(
_contentView
,
layoutDirection
);
}
-
(
void
)
setRemoveClippedSubviews
:
(
__unused
BOOL
)
removeClippedSubviews
{
// Does nothing
}
-
(
void
)
insertReactSubview
:
(
UIView
*
)
view
atIndex
:
(
NSInteger
)
atIndex
{
[
super
insertReactSubview
:
view
atIndex
:
atIndex
];
if
([
view
isKindOfClass
:[
RCTRefreshControl
class
]])
{
[
_scrollView
setRctRefreshControl
:(
RCTRefreshControl
*
)
view
];
}
else
{
RCTAssert
(
_contentView
==
nil
,
@"RCTScrollView may only contain a single subview"
);
_contentView
=
view
;
RCTApplyTransformationAccordingLayoutDirection
(
_contentView
,
self
.
reactLayoutDirection
);
[
_scrollView
addSubview
:
view
];
}
}
-
(
void
)
removeReactSubview
:
(
UIView
*
)
subview
{
[
super
removeReactSubview
:
subview
];
if
([
subview
isKindOfClass
:[
RCTRefreshControl
class
]])
{
[
_scrollView
setRctRefreshControl
:
nil
];
}
else
{
RCTAssert
(
_contentView
==
subview
,
@"Attempted to remove non-existent subview"
);
_contentView
=
nil
;
}
}
-
(
void
)
didUpdateReactSubviews
{
// Do nothing, as subviews are managed by `insertReactSubview:atIndex:`
}
-
(
void
)
didSetProps
:
(
NSArray
<
NSString
*>
*
)
changedProps
{
if
([
changedProps
containsObject
:
@"contentSize"
])
{
[
self
updateContentOffsetIfNeeded
];
}
}
-
(
BOOL
)
centerContent
{
return
_scrollView
.
centerContent
;
}
-
(
void
)
setCenterContent
:
(
BOOL
)
centerContent
{
_scrollView
.
centerContent
=
centerContent
;
}
-
(
void
)
setClipsToBounds
:
(
BOOL
)
clipsToBounds
{
super
.
clipsToBounds
=
clipsToBounds
;
_scrollView
.
clipsToBounds
=
clipsToBounds
;
}
-
(
void
)
dealloc
{
_scrollView
.
delegate
=
nil
;
[
_eventDispatcher
.
bridge
.
uiManager
.
observerCoordinator
removeObserver
:
self
];
// ********************
// add pull to refresh
// ********************
[[
NSNotificationCenter
defaultCenter
]
removeObserver
:
self
];
}
-
(
void
)
layoutSubviews
{
[
super
layoutSubviews
];
RCTAssert
(
self
.
subviews
.
count
==
1
,
@"we should only have exactly one subview"
);
RCTAssert
([
self
.
subviews
lastObject
]
==
_scrollView
,
@"our only subview should be a scrollview"
);
// Adjust the refresh control frame if the scrollview layout changes.
RCTRefreshControl
*
refreshControl
=
_scrollView
.
rctRefreshControl
;
if
(
refreshControl
&&
refreshControl
.
refreshing
)
{
refreshControl
.
frame
=
(
CGRect
){
_scrollView
.
contentOffset
,
{
_scrollView
.
frame
.
size
.
width
,
refreshControl
.
frame
.
size
.
height
}};
}
[
self
updateClippedSubviews
];
}
-
(
void
)
updateClippedSubviews
{
// Find a suitable view to use for clipping
UIView
*
clipView
=
[
self
react_findClipView
];
if
(
!
clipView
)
{
return
;
}
static
const
CGFloat
leeway
=
1
.
0
;
const
CGSize
contentSize
=
_scrollView
.
contentSize
;
const
CGRect
bounds
=
_scrollView
.
bounds
;
const
BOOL
scrollsHorizontally
=
contentSize
.
width
>
bounds
.
size
.
width
;
const
BOOL
scrollsVertically
=
contentSize
.
height
>
bounds
.
size
.
height
;
const
BOOL
shouldClipAgain
=
CGRectIsNull
(
_lastClippedToRect
)
||
!
CGRectEqualToRect
(
_lastClippedToRect
,
bounds
)
||
(
scrollsHorizontally
&&
(
bounds
.
size
.
width
<
leeway
||
fabs
(
_lastClippedToRect
.
origin
.
x
-
bounds
.
origin
.
x
)
>=
leeway
))
||
(
scrollsVertically
&&
(
bounds
.
size
.
height
<
leeway
||
fabs
(
_lastClippedToRect
.
origin
.
y
-
bounds
.
origin
.
y
)
>=
leeway
));
if
(
shouldClipAgain
)
{
const
CGRect
clipRect
=
CGRectInset
(
clipView
.
bounds
,
-
leeway
,
-
leeway
);
[
self
react_updateClippedSubviewsWithClipRect
:
clipRect
relativeToView
:
clipView
];
_lastClippedToRect
=
bounds
;
}
}
-
(
void
)
setContentInset
:
(
UIEdgeInsets
)
contentInset
{
if
(
UIEdgeInsetsEqualToEdgeInsets
(
contentInset
,
_contentInset
))
{
return
;
}
CGPoint
contentOffset
=
_scrollView
.
contentOffset
;
_contentInset
=
contentInset
;
[
RCTView
autoAdjustInsetsForView
:
self
withScrollView
:
_scrollView
updateOffset
:
NO
];
_scrollView
.
contentOffset
=
contentOffset
;
}
-
(
BOOL
)
isHorizontal
:
(
UIScrollView
*
)
scrollView
{
return
scrollView
.
contentSize
.
width
>
self
.
frame
.
size
.
width
;
}
-
(
void
)
scrollToOffset
:
(
CGPoint
)
offset
{
[
self
scrollToOffset
:
offset
animated
:
YES
];
}
-
(
void
)
scrollToOffset
:
(
CGPoint
)
offset
animated
:
(
BOOL
)
animated
{
if
(
!
CGPointEqualToPoint
(
_scrollView
.
contentOffset
,
offset
))
{
// Ensure at least one scroll event will fire
_allowNextScrollNoMatterWhat
=
YES
;
[
_scrollView
setContentOffset
:
offset
animated
:
animated
];
}
}
/**
* If this is a vertical scroll view, scrolls to the bottom.
* If this is a horizontal scroll view, scrolls to the right.
*/
-
(
void
)
scrollToEnd
:
(
BOOL
)
animated
{
BOOL
isHorizontal
=
[
self
isHorizontal
:
_scrollView
];
CGPoint
offset
;
if
(
isHorizontal
)
{
CGFloat
offsetX
=
_scrollView
.
contentSize
.
width
-
_scrollView
.
bounds
.
size
.
width
+
_scrollView
.
contentInset
.
right
;
offset
=
CGPointMake
(
fmax
(
offsetX
,
0
),
0
);
}
else
{
CGFloat
offsetY
=
_scrollView
.
contentSize
.
height
-
_scrollView
.
bounds
.
size
.
height
+
_scrollView
.
contentInset
.
bottom
;
offset
=
CGPointMake
(
0
,
fmax
(
offsetY
,
0
));
}
if
(
!
CGPointEqualToPoint
(
_scrollView
.
contentOffset
,
offset
))
{
// Ensure at least one scroll event will fire
_allowNextScrollNoMatterWhat
=
YES
;
[
_scrollView
setContentOffset
:
offset
animated
:
animated
];
}
}
-
(
void
)
zoomToRect
:
(
CGRect
)
rect
animated
:
(
BOOL
)
animated
{
[
_scrollView
zoomToRect
:
rect
animated
:
animated
];
}
-
(
void
)
refreshContentInset
{
[
RCTView
autoAdjustInsetsForView
:
self
withScrollView
:
_scrollView
updateOffset
:
YES
];
}
#pragma mark - ScrollView delegate
#define RCT_SEND_SCROLL_EVENT(_eventName, _userData) { \
NSString *eventName = NSStringFromSelector(@selector(_eventName)); \
[self sendScrollEventWithName:eventName scrollView:_scrollView userData:_userData]; \
}
#define RCT_FORWARD_SCROLL_EVENT(call) \
for (NSObject<UIScrollViewDelegate> *scrollViewListener in _scrollListeners) { \
if ([scrollViewListener respondsToSelector:_cmd]) { \
[scrollViewListener call]; \
} \
}
#define RCT_SCROLL_EVENT_HANDLER(delegateMethod, eventName) \
- (void)delegateMethod:(UIScrollView *)scrollView \
{ \
RCT_SEND_SCROLL_EVENT(eventName, nil); \
RCT_FORWARD_SCROLL_EVENT(delegateMethod:scrollView); \
}
RCT_SCROLL_EVENT_HANDLER
(
scrollViewWillBeginDecelerating
,
onMomentumScrollBegin
)
RCT_SCROLL_EVENT_HANDLER
(
scrollViewDidZoom
,
onScroll
)
-
(
void
)
addScrollListener
:
(
NSObject
<
UIScrollViewDelegate
>
*
)
scrollListener
{
[
_scrollListeners
addObject
:
scrollListener
];
}
-
(
void
)
removeScrollListener
:
(
NSObject
<
UIScrollViewDelegate
>
*
)
scrollListener
{
[
_scrollListeners
removeObject
:
scrollListener
];
}
-
(
void
)
scrollViewDidScroll
:
(
UIScrollView
*
)
scrollView
{
[
self
updateClippedSubviews
];
NSTimeInterval
now
=
CACurrentMediaTime
();
/**
* TODO: this logic looks wrong, and it may be because it is. Currently, if _scrollEventThrottle
* is set to zero (the default), the "didScroll" event is only sent once per scroll, instead of repeatedly
* while scrolling as expected. However, if you "fix" that bug, ScrollView will generate repeated
* warnings, and behave strangely (ListView works fine however), so don't fix it unless you fix that too!
*/
if
(
_allowNextScrollNoMatterWhat
||
(
_scrollEventThrottle
>
0
&&
_scrollEventThrottle
<
(
now
-
_lastScrollDispatchTime
)))
{
if
(
_DEPRECATED_sendUpdatedChildFrames
)
{
// Calculate changed frames
RCT_SEND_SCROLL_EVENT
(
onScroll
,
(
@
{
@"updatedChildFrames"
:
[
self
calculateChildFramesData
]}));
}
else
{
RCT_SEND_SCROLL_EVENT
(
onScroll
,
nil
);
}
// Update dispatch time
_lastScrollDispatchTime
=
now
;
_allowNextScrollNoMatterWhat
=
NO
;
}
RCT_FORWARD_SCROLL_EVENT
(
scrollViewDidScroll
:
scrollView
);
}
-
(
NSArray
<
NSDictionary
*>
*
)
calculateChildFramesData
{
NSMutableArray
<
NSDictionary
*>
*
updatedChildFrames
=
[
NSMutableArray
new
];
[[
_contentView
reactSubviews
]
enumerateObjectsUsingBlock
:
^
(
UIView
*
subview
,
NSUInteger
idx
,
__unused
BOOL
*
stop
)
{
// Check if new or changed
CGRect
newFrame
=
subview
.
frame
;
BOOL
frameChanged
=
NO
;
if
(
self
->
_cachedChildFrames
.
count
<=
idx
)
{
frameChanged
=
YES
;
[
self
->
_cachedChildFrames
addObject
:[
NSValue
valueWithCGRect
:
newFrame
]]
;
}
else
if
(
!
CGRectEqualToRect
(
newFrame
,
[
self
->
_cachedChildFrames
[
idx
]
CGRectValue
]))
{
frameChanged
=
YES
;
self
->
_cachedChildFrames
[
idx
]
=
[
NSValue
valueWithCGRect
:
newFrame
]
;
}
// Create JS frame object
if
(
frameChanged
)
{
[
updatedChildFrames
addObject
:
@
{
@"index"
:
@
(
idx
),
@"x"
:
@
(
newFrame
.
origin
.
x
),
@"y"
:
@
(
newFrame
.
origin
.
y
),
@"width"
:
@
(
newFrame
.
size
.
width
),
@"height"
:
@
(
newFrame
.
size
.
height
),
}]
;
}
}];
return
updatedChildFrames
;
}
-
(
void
)
scrollViewWillBeginDragging
:
(
UIScrollView
*
)
scrollView
{
_allowNextScrollNoMatterWhat
=
YES
;
// Ensure next scroll event is recorded, regardless of throttle
RCT_SEND_SCROLL_EVENT
(
onScrollBeginDrag
,
nil
);
RCT_FORWARD_SCROLL_EVENT
(
scrollViewWillBeginDragging
:
scrollView
);
}
-
(
void
)
scrollViewWillEndDragging
:
(
UIScrollView
*
)
scrollView
withVelocity
:
(
CGPoint
)
velocity
targetContentOffset
:
(
inout
CGPoint
*
)
targetContentOffset
{
// snapToInterval
// An alternative to enablePaging which allows setting custom stopping intervals,
// smaller than a full page size. Often seen in apps which feature horizonally
// scrolling items. snapToInterval does not enforce scrolling one interval at a time
// but guarantees that the scroll will stop at an interval point.
if
(
self
.
snapToInterval
)
{
CGFloat
snapToIntervalF
=
(
CGFloat
)
self
.
snapToInterval
;
// Find which axis to snap
BOOL
isHorizontal
=
[
self
isHorizontal
:
scrollView
];
// What is the current offset?
CGFloat
velocityAlongAxis
=
isHorizontal
?
velocity
.
x
:
velocity
.
y
;
CGFloat
targetContentOffsetAlongAxis
=
isHorizontal
?
targetContentOffset
->
x
:
targetContentOffset
->
y
;
// Offset based on desired alignment
CGFloat
frameLength
=
isHorizontal
?
self
.
frame
.
size
.
width
:
self
.
frame
.
size
.
height
;
CGFloat
alignmentOffset
=
0
.
0
f
;
if
([
self
.
snapToAlignment
isEqualToString
:
@"center"
])
{
alignmentOffset
=
(
frameLength
*
0
.
5
f
)
+
(
snapToIntervalF
*
0
.
5
f
);
}
else
if
([
self
.
snapToAlignment
isEqualToString
:
@"end"
])
{
alignmentOffset
=
frameLength
;
}
// Pick snap point based on direction and proximity
CGFloat
fractionalIndex
=
(
targetContentOffsetAlongAxis
+
alignmentOffset
)
/
snapToIntervalF
;
NSInteger
snapIndex
=
velocityAlongAxis
>
0
.
0
?
ceil
(
fractionalIndex
)
:
velocityAlongAxis
<
0
.
0
?
floor
(
fractionalIndex
)
:
round
(
fractionalIndex
);
CGFloat
newTargetContentOffset
=
(
snapIndex
*
snapToIntervalF
)
-
alignmentOffset
;
// Set new targetContentOffset
if
(
isHorizontal
)
{
targetContentOffset
->
x
=
newTargetContentOffset
;
}
else
{
targetContentOffset
->
y
=
newTargetContentOffset
;
}
}
NSDictionary
*
userData
=
@
{
@"velocity"
:
@
{
@"x"
:
@
(
velocity
.
x
),
@"y"
:
@
(
velocity
.
y
)
},
@"targetContentOffset"
:
@
{
@"x"
:
@
(
targetContentOffset
->
x
),
@"y"
:
@
(
targetContentOffset
->
y
)
}
};
RCT_SEND_SCROLL_EVENT
(
onScrollEndDrag
,
userData
);
RCT_FORWARD_SCROLL_EVENT
(
scrollViewWillEndDragging
:
scrollView
withVelocity
:
velocity
targetContentOffset
:
targetContentOffset
);
}
-
(
void
)
scrollViewDidEndDragging
:
(
UIScrollView
*
)
scrollView
willDecelerate
:
(
BOOL
)
decelerate
{
RCT_FORWARD_SCROLL_EVENT
(
scrollViewDidEndDragging
:
scrollView
willDecelerate
:
decelerate
);
}
-
(
void
)
scrollViewWillBeginZooming
:
(
UIScrollView
*
)
scrollView
withView
:
(
UIView
*
)
view
{
RCT_SEND_SCROLL_EVENT
(
onScrollBeginDrag
,
nil
);
RCT_FORWARD_SCROLL_EVENT
(
scrollViewWillBeginZooming
:
scrollView
withView
:
view
);
}
-
(
void
)
scrollViewDidEndZooming
:
(
UIScrollView
*
)
scrollView
withView
:
(
UIView
*
)
view
atScale
:
(
CGFloat
)
scale
{
RCT_SEND_SCROLL_EVENT
(
onScrollEndDrag
,
nil
);
RCT_FORWARD_SCROLL_EVENT
(
scrollViewDidEndZooming
:
scrollView
withView
:
view
atScale
:
scale
);
}
-
(
void
)
scrollViewDidEndDecelerating
:
(
UIScrollView
*
)
scrollView
{
// Fire a final scroll event
_allowNextScrollNoMatterWhat
=
YES
;
[
self
scrollViewDidScroll
:
scrollView
];
// Fire the end deceleration event
RCT_SEND_SCROLL_EVENT
(
onMomentumScrollEnd
,
nil
);
RCT_FORWARD_SCROLL_EVENT
(
scrollViewDidEndDecelerating
:
scrollView
);
}
-
(
void
)
scrollViewDidEndScrollingAnimation
:
(
UIScrollView
*
)
scrollView
{
// Fire a final scroll event
_allowNextScrollNoMatterWhat
=
YES
;
[
self
scrollViewDidScroll
:
scrollView
];
// Fire the end deceleration event
RCT_SEND_SCROLL_EVENT
(
onMomentumScrollEnd
,
nil
);
RCT_FORWARD_SCROLL_EVENT
(
scrollViewDidEndScrollingAnimation
:
scrollView
);
}
-
(
BOOL
)
scrollViewShouldScrollToTop
:
(
UIScrollView
*
)
scrollView
{
for
(
NSObject
<
UIScrollViewDelegate
>
*
scrollListener
in
_scrollListeners
)
{
if
([
scrollListener
respondsToSelector
:
_cmd
]
&&
!
[
scrollListener
scrollViewShouldScrollToTop
:
scrollView
])
{
return
NO
;
}
}
return
YES
;
}
-
(
UIView
*
)
viewForZoomingInScrollView
:
(
__unused
UIScrollView
*
)
scrollView
{
return
_contentView
;
}
#pragma mark - Setters
-
(
CGSize
)
_calculateViewportSize
{
CGSize
viewportSize
=
self
.
bounds
.
size
;
if
(
_automaticallyAdjustContentInsets
)
{
UIEdgeInsets
contentInsets
=
[
RCTView
contentInsetsForView
:
self
];
viewportSize
=
CGSizeMake
(
self
.
bounds
.
size
.
width
-
contentInsets
.
left
-
contentInsets
.
right
,
self
.
bounds
.
size
.
height
-
contentInsets
.
top
-
contentInsets
.
bottom
);
}
return
viewportSize
;
}
-
(
CGPoint
)
calculateOffsetForContentSize
:
(
CGSize
)
newContentSize
{
CGPoint
oldOffset
=
_scrollView
.
contentOffset
;
CGPoint
newOffset
=
oldOffset
;
CGSize
oldContentSize
=
_scrollView
.
contentSize
;
CGSize
viewportSize
=
[
self
_calculateViewportSize
];
BOOL
fitsinViewportY
=
oldContentSize
.
height
<=
viewportSize
.
height
&&
newContentSize
.
height
<=
viewportSize
.
height
;
if
(
newContentSize
.
height
<
oldContentSize
.
height
&&
!
fitsinViewportY
)
{
CGFloat
offsetHeight
=
oldOffset
.
y
+
viewportSize
.
height
;
if
(
oldOffset
.
y
<
0
)
{
// overscrolled on top, leave offset alone
}
else
if
(
offsetHeight
>
oldContentSize
.
height
)
{
// overscrolled on the bottom, preserve overscroll amount
newOffset
.
y
=
MAX
(
0
,
oldOffset
.
y
-
(
oldContentSize
.
height
-
newContentSize
.
height
));
}
else
if
(
offsetHeight
>
newContentSize
.
height
)
{
// offset falls outside of bounds, scroll back to end of list
newOffset
.
y
=
MAX
(
0
,
newContentSize
.
height
-
viewportSize
.
height
);
}
}
BOOL
fitsinViewportX
=
oldContentSize
.
width
<=
viewportSize
.
width
&&
newContentSize
.
width
<=
viewportSize
.
width
;
if
(
newContentSize
.
width
<
oldContentSize
.
width
&&
!
fitsinViewportX
)
{
CGFloat
offsetHeight
=
oldOffset
.
x
+
viewportSize
.
width
;
if
(
oldOffset
.
x
<
0
)
{
// overscrolled at the beginning, leave offset alone
}
else
if
(
offsetHeight
>
oldContentSize
.
width
&&
newContentSize
.
width
>
viewportSize
.
width
)
{
// overscrolled at the end, preserve overscroll amount as much as possible
newOffset
.
x
=
MAX
(
0
,
oldOffset
.
x
-
(
oldContentSize
.
width
-
newContentSize
.
width
));
}
else
if
(
offsetHeight
>
newContentSize
.
width
)
{
// offset falls outside of bounds, scroll back to end
newOffset
.
x
=
MAX
(
0
,
newContentSize
.
width
-
viewportSize
.
width
);
}
}
// all other cases, offset doesn't change
return
newOffset
;
}
/**
* Once you set the `contentSize`, to a nonzero value, it is assumed to be
* managed by you, and we'll never automatically compute the size for you,
* unless you manually reset it back to {0, 0}
*/
-
(
CGSize
)
contentSize
{
if
(
!
CGSizeEqualToSize
(
_contentSize
,
CGSizeZero
))
{
return
_contentSize
;
}
return
_contentView
.
frame
.
size
;
}
-
(
void
)
updateContentOffsetIfNeeded
{
CGSize
contentSize
=
self
.
contentSize
;
if
(
!
CGSizeEqualToSize
(
_scrollView
.
contentSize
,
contentSize
))
{
// When contentSize is set manually, ScrollView internals will reset
// contentOffset to {0, 0}. Since we potentially set contentSize whenever
// anything in the ScrollView updates, we workaround this issue by manually
// adjusting contentOffset whenever this happens
CGPoint
newOffset
=
[
self
calculateOffsetForContentSize
:
contentSize
];
_scrollView
.
contentSize
=
contentSize
;
_scrollView
.
contentOffset
=
newOffset
;
}
}
// maintainVisibleContentPosition is used to allow seamless loading of content from both ends of
// the scrollview without the visible content jumping in position.
-
(
void
)
setMaintainVisibleContentPosition
:
(
NSDictionary
*
)
maintainVisibleContentPosition
{
if
(
maintainVisibleContentPosition
!=
nil
&&
_maintainVisibleContentPosition
==
nil
)
{
[
_eventDispatcher
.
bridge
.
uiManager
.
observerCoordinator
addObserver
:
self
];
}
else
if
(
maintainVisibleContentPosition
==
nil
&&
_maintainVisibleContentPosition
!=
nil
)
{
[
_eventDispatcher
.
bridge
.
uiManager
.
observerCoordinator
removeObserver
:
self
];
}
_maintainVisibleContentPosition
=
maintainVisibleContentPosition
;
}
#pragma mark - RCTUIManagerObserver
-
(
void
)
uiManagerWillPerformMounting
:
(
RCTUIManager
*
)
manager
{
RCTAssertUIManagerQueue
();
[
manager
prependUIBlock
:
^
(
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
UIView
*>
*
viewRegistry
)
{
BOOL
horz
=
[
self
isHorizontal
:
self
->
_scrollView
]
;
NSUInteger
minIdx
=
[
self
->
_maintainVisibleContentPosition
[
@"minIndexForVisible"
]
integerValue
]
;
for
(
NSUInteger
ii
=
minIdx
;
ii
<
self
->
_contentView
.
subviews
.
count
;
++
ii
)
{
// Find the first entirely visible view. This must be done after we update the content offset
// or it will tend to grab rows that were made visible by the shift in position
UIView
*
subview
=
self
->
_contentView
.
subviews
[
ii
]
;
if
((
horz
?
subview
.
frame
.
origin
.
x
>=
self
->
_scrollView
.
contentOffset
.
x
:
subview
.
frame
.
origin
.
y
>=
self
->
_scrollView
.
contentOffset
.
y
)
||
ii
==
self
->
_contentView
.
subviews
.
count
-
1
)
{
self
->
_prevFirstVisibleFrame
=
subview
.
frame
;
self
->
_firstVisibleView
=
subview
;
break
;
}
}
}];
[
manager
addUIBlock
:
^
(
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
UIView
*>
*
viewRegistry
)
{
if
(
self
->
_maintainVisibleContentPosition
==
nil
)
{
return
;
// The prop might have changed in the previous UIBlocks, so need to abort here.
}
NSNumber
*
autoscrollThreshold
=
self
->
_maintainVisibleContentPosition
[
@"autoscrollToTopThreshold"
]
;
// TODO: detect and handle/ignore re-ordering
if
([
self
isHorizontal
:
self
->
_scrollView
])
{
CGFloat
deltaX
=
self
->
_firstVisibleView
.
frame
.
origin
.
x
-
self
->
_prevFirstVisibleFrame
.
origin
.
x
;
if
(
ABS
(
deltaX
)
>
0
.
1
)
{
self
->
_scrollView
.
contentOffset
=
CGPointMake
(
self
->
_scrollView
.
contentOffset
.
x
+
deltaX
,
self
->
_scrollView
.
contentOffset
.
y
)
;
if
(
autoscrollThreshold
!=
nil
)
{
// If the offset WAS within the threshold of the start, animate to the start.
if
(
self
->
_scrollView
.
contentOffset
.
x
-
deltaX
<=
[
autoscrollThreshold
integerValue
])
{
[
self
scrollToOffset
:
CGPointMake
(
0
,
self
->
_scrollView
.
contentOffset
.
y
)
animated
:
YES
]
;
}
}
}
}
else
{
CGRect
newFrame
=
self
->
_firstVisibleView
.
frame
;
CGFloat
deltaY
=
newFrame
.
origin
.
y
-
self
->
_prevFirstVisibleFrame
.
origin
.
y
;
if
(
ABS
(
deltaY
)
>
0
.
1
)
{
self
->
_scrollView
.
contentOffset
=
CGPointMake
(
self
->
_scrollView
.
contentOffset
.
x
,
self
->
_scrollView
.
contentOffset
.
y
+
deltaY
)
;
if
(
autoscrollThreshold
!=
nil
)
{
// If the offset WAS within the threshold of the start, animate to the start.
if
(
self
->
_scrollView
.
contentOffset
.
y
-
deltaY
<=
[
autoscrollThreshold
integerValue
])
{
[
self
scrollToOffset
:
CGPointMake
(
self
->
_scrollView
.
contentOffset
.
x
,
0
)
animated
:
YES
]
;
}
}
}
}
}];
}
// Note: setting several properties of UIScrollView has the effect of
// resetting its contentOffset to {0, 0}. To prevent this, we generate
// setters here that will record the contentOffset beforehand, and
// restore it after the property has been set.
#define RCT_SET_AND_PRESERVE_OFFSET(setter, getter, type) \
- (void)setter:(type)value \
{ \
CGPoint contentOffset = _scrollView.contentOffset; \
[_scrollView setter:value]; \
_scrollView.contentOffset = contentOffset; \
} \
- (type)getter \
{ \
return [_scrollView getter]; \
}
RCT_SET_AND_PRESERVE_OFFSET
(
setAlwaysBounceHorizontal
,
alwaysBounceHorizontal
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setAlwaysBounceVertical
,
alwaysBounceVertical
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setBounces
,
bounces
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setBouncesZoom
,
bouncesZoom
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setCanCancelContentTouches
,
canCancelContentTouches
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setDecelerationRate
,
decelerationRate
,
CGFloat
)
RCT_SET_AND_PRESERVE_OFFSET
(
setDirectionalLockEnabled
,
isDirectionalLockEnabled
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setIndicatorStyle
,
indicatorStyle
,
UIScrollViewIndicatorStyle
)
RCT_SET_AND_PRESERVE_OFFSET
(
setKeyboardDismissMode
,
keyboardDismissMode
,
UIScrollViewKeyboardDismissMode
)
RCT_SET_AND_PRESERVE_OFFSET
(
setMaximumZoomScale
,
maximumZoomScale
,
CGFloat
)
RCT_SET_AND_PRESERVE_OFFSET
(
setMinimumZoomScale
,
minimumZoomScale
,
CGFloat
)
RCT_SET_AND_PRESERVE_OFFSET
(
setScrollEnabled
,
isScrollEnabled
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setPagingEnabled
,
isPagingEnabled
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setScrollsToTop
,
scrollsToTop
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setShowsHorizontalScrollIndicator
,
showsHorizontalScrollIndicator
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setShowsVerticalScrollIndicator
,
showsVerticalScrollIndicator
,
BOOL
)
RCT_SET_AND_PRESERVE_OFFSET
(
setZoomScale
,
zoomScale
,
CGFloat
);
RCT_SET_AND_PRESERVE_OFFSET
(
setScrollIndicatorInsets
,
scrollIndicatorInsets
,
UIEdgeInsets
);
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
/* __IPHONE_11_0 */
-
(
void
)
setContentInsetAdjustmentBehavior
:(
UIScrollViewContentInsetAdjustmentBehavior
)
behavior
{
// `contentInsetAdjustmentBehavior` is available since iOS 11.
if
([
_scrollView
respondsToSelector
:
@selector
(
setContentInsetAdjustmentBehavior
:)])
{
CGPoint
contentOffset
=
_scrollView
.
contentOffset
;
_scrollView
.
contentInsetAdjustmentBehavior
=
behavior
;
_scrollView
.
contentOffset
=
contentOffset
;
}
}
#endif
-
(
void
)
sendScrollEventWithName
:(
NSString
*
)
eventName
scrollView
:(
UIScrollView
*
)
scrollView
userData
:(
NSDictionary
*
)
userData
{
if
(
!
[
_lastEmittedEventName
isEqualToString
:
eventName
])
{
_coalescingKey
++
;
_lastEmittedEventName
=
[
eventName
copy
];
}
RCTScrollEvent
*
scrollEvent
=
[[
RCTScrollEvent
alloc
]
initWithEventName
:
eventName
reactTag
:
self
.
reactTag
scrollViewContentOffset
:
scrollView
.
contentOffset
scrollViewContentInset
:
scrollView
.
contentInset
scrollViewContentSize
:
scrollView
.
contentSize
scrollViewFrame
:
scrollView
.
frame
scrollViewZoomScale
:
scrollView
.
zoomScale
userData
:
userData
coalescingKey
:
_coalescingKey
];
[
_eventDispatcher
sendEvent
:
scrollEvent
];
}
// ********************
// add pull to refresh
// ********************
-
(
void
)
startPullToRefresh
{
self
.
isOnPullToRefresh
=
YES
;
[
_scrollView
.
header
beginRefreshing
];
}
// ********************
// add pull to refresh
// ********************
-
(
void
)
stopPullToRefresh
{
self
.
isOnPullToRefresh
=
NO
;
[
_scrollView
.
header
endRefreshing
];
if
(
_onFinishRefreshData
)
{
_onFinishRefreshData
(
nil
);
}
}
// ********************
// add pull to refresh
// ********************
-
(
void
)
setEnablePullToRefresh
:
(
BOOL
)
enablePullToRefresh
{
_enablePullToRefresh
=
enablePullToRefresh
;
if
(
enablePullToRefresh
)
{
if
(
_scrollView
.
header
==
nil
)
{
[
_scrollView
addGifHeaderWithRefreshingTarget
:
self
refreshingAction
:
@selector
(
refreshData
)];
[
self
customRefreshHeaderView
];
self
.
currentRefreshingState
=
NO
;
}
}
}
-
(
void
)
setIsOnPullToRefresh
:
(
BOOL
)
isOnPullToRefresh
{
if
(
_currentRefreshingState
!=
isOnPullToRefresh
)
{
_currentRefreshingState
=
isOnPullToRefresh
;
if
(
isOnPullToRefresh
)
{
[
_scrollView
.
header
beginRefreshing
];
}
else
{
[
_scrollView
.
header
endRefreshing
];
if
(
_onFinishRefreshData
)
{
_onFinishRefreshData
(
nil
);
}
}
}
}
// ********************
// add pull to refresh
// ********************
-
(
void
)
refreshData
{
_currentRefreshingState
=
_scrollView
.
header
.
isRefreshing
;
if
(
_onRefreshData
)
{
_onRefreshData
(
nil
);
}
}
// ********************
// add pull to refresh
// ********************
-
(
void
)
setRefreshHeaderImage
{
// 设置普通状态的动画图片
NSString
*
idleImageName
=
[
NSString
stringWithFormat
:
@"refresh_000"
];
UIImage
*
idleImage
=
[[
YH_ThemeManager
shareInstance
]
getThemeImage
:
idleImageName
];
[
_scrollView
.
gifHeader
setImages
:@[
idleImage
]
forState
:
MJRefreshHeaderStateIdle
];
// 设置正在刷新状态的动画图片
NSMutableArray
*
refreshingImages
=
[
NSMutableArray
array
];
for
(
NSUInteger
i
=
1
;
i
<=
12
;
i
++
)
{
NSString
*
imageName
=
[
NSString
stringWithFormat
:
@"refresh_%03zd"
,
i
];
UIImage
*
image
=
[[
YH_ThemeManager
shareInstance
]
getThemeImage
:
imageName
];
[
refreshingImages
addObject
:
image
];
}
[
_scrollView
.
gifHeader
setImages
:
refreshingImages
forState
:
MJRefreshHeaderStateRefreshing
];
}
// ********************
// add pull to refresh
// ********************
-
(
void
)
customRefreshHeaderView
{
// 隐藏时间
_scrollView
.
header
.
updatedTimeHidden
=
YES
;
// 隐藏状态
_scrollView
.
header
.
stateHidden
=
YES
;
[
self
setRefreshHeaderImage
];
}
// ********************
// add pull to refresh
// ********************
-
(
void
)
themeSwitchNotificationHandle
:
(
NSNotification
*
)
notification
{
if
(
_scrollView
.
header
.
isRefreshing
)
{
[
_scrollView
.
header
endRefreshing
];
if
(
_onFinishRefreshData
)
{
_onFinishRefreshData
(
nil
);
}
}
[
self
setRefreshHeaderImage
];
}
// ********************
// add pull to refresh
// ********************
@end
@implementation
RCTEventDispatcher
(
RCTScrollView
)
-
(
void
)
sendFakeScrollEvent
:
(
NSNumber
*
)
reactTag
{
// Use the selector here in case the onScroll block property is ever renamed
NSString
*
eventName
=
NSStringFromSelector
(
@selector
(
onScroll
));
RCTScrollEvent
*
fakeScrollEvent
=
[[
RCTScrollEvent
alloc
]
initWithEventName
:
eventName
reactTag
:
reactTag
scrollViewContentOffset
:
CGPointZero
scrollViewContentInset
:
UIEdgeInsetsZero
scrollViewContentSize
:
CGSizeZero
scrollViewFrame
:
CGRectZero
scrollViewZoomScale
:
0
userData
:
nil
coalescingKey
:
0
];
[
self
sendEvent
:
fakeScrollEvent
];
}
@end
React/Views/ScrollView/RCTScrollViewManager.h
deleted
100755 → 0
View file @
45548ec
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <React/RCTConvert.h>
#import <React/RCTViewManager.h>
@interface
RCTConvert
(
UIScrollView
)
+
(
UIScrollViewKeyboardDismissMode
)
UIScrollViewKeyboardDismissMode
:
(
id
)
json
;
@end
@interface
RCTScrollViewManager
:
RCTViewManager
@end
React/Views/ScrollView/RCTScrollViewManager.m
deleted
100755 → 0
View file @
45548ec
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTScrollViewManager.h"
#import <React/RCTBridge.h>
#import "RCTScrollView.h"
#import <React/RCTShadowView.h>
#import <React/RCTUIManager.h>
@interface
RCTScrollView
(
Private
)
-
(
NSArray
<
NSDictionary
*>
*
)
calculateChildFramesData
;
@end
@implementation
RCTConvert
(
UIScrollView
)
RCT_ENUM_CONVERTER
(
UIScrollViewKeyboardDismissMode
,
(
@
{
@"none"
:
@
(
UIScrollViewKeyboardDismissModeNone
),
@"on-drag"
:
@
(
UIScrollViewKeyboardDismissModeOnDrag
),
@"interactive"
:
@
(
UIScrollViewKeyboardDismissModeInteractive
),
// Backwards compatibility
@"onDrag"
:
@
(
UIScrollViewKeyboardDismissModeOnDrag
),
}),
UIScrollViewKeyboardDismissModeNone
,
integerValue
)
RCT_ENUM_CONVERTER
(
UIScrollViewIndicatorStyle
,
(
@
{
@"default"
:
@
(
UIScrollViewIndicatorStyleDefault
),
@"black"
:
@
(
UIScrollViewIndicatorStyleBlack
),
@"white"
:
@
(
UIScrollViewIndicatorStyleWhite
),
}),
UIScrollViewIndicatorStyleDefault
,
integerValue
)
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
/* __IPHONE_11_0 */
RCT_ENUM_CONVERTER
(
UIScrollViewContentInsetAdjustmentBehavior
,
(
@
{
@"automatic"
:
@
(
UIScrollViewContentInsetAdjustmentAutomatic
),
@"scrollableAxes"
:
@
(
UIScrollViewContentInsetAdjustmentScrollableAxes
),
@"never"
:
@
(
UIScrollViewContentInsetAdjustmentNever
),
@"always"
:
@
(
UIScrollViewContentInsetAdjustmentAlways
),
}),
UIScrollViewContentInsetAdjustmentNever
,
integerValue
)
#endif
@end
@implementation
RCTScrollViewManager
RCT_EXPORT_MODULE
()
-
(
UIView
*
)
view
{
return
[[
RCTScrollView
alloc
]
initWithEventDispatcher
:
self
.
bridge
.
eventDispatcher
];
}
RCT_EXPORT_VIEW_PROPERTY
(
alwaysBounceHorizontal
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
alwaysBounceVertical
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
bounces
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
bouncesZoom
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
canCancelContentTouches
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
centerContent
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
maintainVisibleContentPosition
,
NSDictionary
)
RCT_EXPORT_VIEW_PROPERTY
(
automaticallyAdjustContentInsets
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
decelerationRate
,
CGFloat
)
RCT_EXPORT_VIEW_PROPERTY
(
directionalLockEnabled
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
indicatorStyle
,
UIScrollViewIndicatorStyle
)
RCT_EXPORT_VIEW_PROPERTY
(
keyboardDismissMode
,
UIScrollViewKeyboardDismissMode
)
RCT_EXPORT_VIEW_PROPERTY
(
maximumZoomScale
,
CGFloat
)
RCT_EXPORT_VIEW_PROPERTY
(
minimumZoomScale
,
CGFloat
)
RCT_EXPORT_VIEW_PROPERTY
(
scrollEnabled
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
pagingEnabled
,
BOOL
)
RCT_REMAP_VIEW_PROPERTY
(
pinchGestureEnabled
,
scrollView
.
pinchGestureEnabled
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
scrollsToTop
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
showsHorizontalScrollIndicator
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
showsVerticalScrollIndicator
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
scrollEventThrottle
,
NSTimeInterval
)
RCT_EXPORT_VIEW_PROPERTY
(
zoomScale
,
CGFloat
)
RCT_EXPORT_VIEW_PROPERTY
(
contentInset
,
UIEdgeInsets
)
RCT_EXPORT_VIEW_PROPERTY
(
scrollIndicatorInsets
,
UIEdgeInsets
)
RCT_EXPORT_VIEW_PROPERTY
(
snapToInterval
,
int
)
RCT_EXPORT_VIEW_PROPERTY
(
snapToAlignment
,
NSString
)
RCT_REMAP_VIEW_PROPERTY
(
contentOffset
,
scrollView
.
contentOffset
,
CGPoint
)
RCT_EXPORT_VIEW_PROPERTY
(
onScrollBeginDrag
,
RCTDirectEventBlock
)
RCT_EXPORT_VIEW_PROPERTY
(
onScroll
,
RCTDirectEventBlock
)
RCT_EXPORT_VIEW_PROPERTY
(
onScrollEndDrag
,
RCTDirectEventBlock
)
RCT_EXPORT_VIEW_PROPERTY
(
onMomentumScrollBegin
,
RCTDirectEventBlock
)
RCT_EXPORT_VIEW_PROPERTY
(
onMomentumScrollEnd
,
RCTDirectEventBlock
)
RCT_EXPORT_VIEW_PROPERTY
(
DEPRECATED_sendUpdatedChildFrames
,
BOOL
)
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
/* __IPHONE_11_0 */
RCT_EXPORT_VIEW_PROPERTY
(
contentInsetAdjustmentBehavior
,
UIScrollViewContentInsetAdjustmentBehavior
)
#endif
// overflow is used both in css-layout as well as by react-native. In css-layout
// we always want to treat overflow as scroll but depending on what the overflow
// is set to from js we want to clip drawing or not. This piece of code ensures
// that css-layout is always treating the contents of a scroll container as
// overflow: 'scroll'.
RCT_CUSTOM_SHADOW_PROPERTY
(
overflow
,
YGOverflow
,
RCTShadowView
)
{
#pragma unused (json)
view
.
overflow
=
YGOverflowScroll
;
}
RCT_EXPORT_METHOD
(
getContentSize
:
(
nonnull
NSNumber
*
)
reactTag
callback
:
(
RCTResponseSenderBlock
)
callback
)
{
[
self
.
bridge
.
uiManager
addUIBlock
:
^
(
__unused
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
RCTScrollView
*>
*
viewRegistry
)
{
RCTScrollView
*
view
=
viewRegistry
[
reactTag
]
;
if
(
!
view
||
!
[
view
isKindOfClass
:[
RCTScrollView
class
]])
{
RCTLogError
(
@"Cannot find RCTScrollView with tag #%@"
,
reactTag
)
;
return
;
}
CGSize
size
=
view
.
scrollView
.
contentSize
;
callback
(@[
@{
@"width"
:
@
(
size
.
width
),
@"height"
:
@
(
size
.
height
)
}
])
;
}];
}
RCT_EXPORT_METHOD
(
calculateChildFrames
:
(
nonnull
NSNumber
*
)
reactTag
callback
:
(
RCTResponseSenderBlock
)
callback
)
{
[
self
.
bridge
.
uiManager
addUIBlock
:
^
(
__unused
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
RCTScrollView
*>
*
viewRegistry
)
{
RCTScrollView
*
view
=
viewRegistry
[
reactTag
]
;
if
(
!
view
||
!
[
view
isKindOfClass
:[
RCTScrollView
class
]])
{
RCTLogError
(
@"Cannot find RCTScrollView with tag #%@"
,
reactTag
)
;
return
;
}
NSArray
<
NSDictionary
*>
*
childFrames
=
[
view
calculateChildFramesData
]
;
if
(
childFrames
)
{
callback
(@[
childFrames
])
;
}
}];
}
RCT_EXPORT_METHOD
(
scrollTo
:
(
nonnull
NSNumber
*
)
reactTag
offsetX
:
(
CGFloat
)
x
offsetY
:
(
CGFloat
)
y
animated
:
(
BOOL
)
animated
)
{
[
self
.
bridge
.
uiManager
addUIBlock
:
^
(
__unused
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
UIView
*>
*
viewRegistry
){
UIView
*
view
=
viewRegistry
[
reactTag
]
;
if
([
view
conformsToProtocol
:
@protocol
(
RCTScrollableProtocol
)])
{
[(
id
<
RCTScrollableProtocol
>
)
view
scrollToOffset
:(
CGPoint
){
x
,
y
}
animated
:
animated
];
}
else
{
RCTLogError
(
@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
"with tag #%@"
,
view
,
reactTag
);
}
}];
}
RCT_EXPORT_METHOD
(
scrollToEnd
:
(
nonnull
NSNumber
*
)
reactTag
animated
:
(
BOOL
)
animated
)
{
[
self
.
bridge
.
uiManager
addUIBlock
:
^
(
__unused
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
UIView
*>
*
viewRegistry
){
UIView
*
view
=
viewRegistry
[
reactTag
]
;
if
([
view
conformsToProtocol
:
@protocol
(
RCTScrollableProtocol
)])
{
[(
id
<
RCTScrollableProtocol
>
)
view
scrollToEnd
:
animated
];
}
else
{
RCTLogError
(
@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
"with tag #%@"
,
view
,
reactTag
);
}
}];
}
RCT_EXPORT_METHOD
(
zoomToRect
:
(
nonnull
NSNumber
*
)
reactTag
withRect
:
(
CGRect
)
rect
animated
:
(
BOOL
)
animated
)
{
[
self
.
bridge
.
uiManager
addUIBlock
:
^
(
__unused
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
UIView
*>
*
viewRegistry
){
UIView
*
view
=
viewRegistry
[
reactTag
]
;
if
([
view
conformsToProtocol
:
@protocol
(
RCTScrollableProtocol
)])
{
[(
id
<
RCTScrollableProtocol
>
)
view
zoomToRect
:
rect
animated
:
animated
];
}
else
{
RCTLogError
(
@"tried to zoomToRect: on non-RCTScrollableProtocol view %@ "
"with tag #%@"
,
view
,
reactTag
);
}
}];
}
RCT_EXPORT_METHOD
(
flashScrollIndicators
:
(
nonnull
NSNumber
*
)
reactTag
)
{
[
self
.
bridge
.
uiManager
addUIBlock
:
^
(
__unused
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
RCTScrollView
*>
*
viewRegistry
){
RCTScrollView
*
view
=
viewRegistry
[
reactTag
]
;
if
(
!
view
||
!
[
view
isKindOfClass
:[
RCTScrollView
class
]])
{
RCTLogError
(
@"Cannot find RCTScrollView with tag #%@"
,
reactTag
)
;
return
;
}
[
view
.
scrollView
flashScrollIndicators
]
;
}];
}
// ********************
// add pull to refresh
// ********************
RCT_EXPORT_VIEW_PROPERTY
(
onRefreshData
,
RCTDirectEventBlock
)
RCT_EXPORT_VIEW_PROPERTY
(
isOnPullToRefresh
,
BOOL
)
RCT_EXPORT_VIEW_PROPERTY
(
enablePullToRefresh
,
BOOL
)
RCT_EXPORT_METHOD
(
stopPullToRefresh
:
(
nonnull
NSNumber
*
)
reactTag
)
{
[
self
.
bridge
.
uiManager
addUIBlock
:
^
(
__unused
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
RCTScrollView
*>
*
viewRegistry
)
{
RCTScrollView
*
view
=
viewRegistry
[
reactTag
]
;
if
(
!
view
||
!
[
view
isKindOfClass
:[
RCTScrollView
class
]])
{
RCTLogError
(
@"Cannot find RCTScrollView with tag #%@"
,
reactTag
)
;
return
;
}
[
view
stopPullToRefresh
]
;
}];
}
RCT_EXPORT_METHOD
(
startPullToRefresh
:
(
nonnull
NSNumber
*
)
reactTag
)
{
[
self
.
bridge
.
uiManager
addUIBlock
:
^
(
__unused
RCTUIManager
*
uiManager
,
NSDictionary
<
NSNumber
*
,
RCTScrollView
*>
*
viewRegistry
)
{
RCTScrollView
*
view
=
viewRegistry
[
reactTag
]
;
if
(
!
view
||
!
[
view
isKindOfClass
:[
RCTScrollView
class
]])
{
RCTLogError
(
@"Cannot find RCTScrollView with tag #%@"
,
reactTag
)
;
return
;
}
[
view
startPullToRefresh
]
;
}];
}
// ********************
// add pull to refresh
// ********************
@end
张丽霞
@zhanglixia
7 years ago
Master
mentioned in commit
84e3def1
Please
register
or
login
to post a comment