在 UIScrollView 嵌套一 中存在 拖拽时会中断的情况 这是因为手势中断引起的。所以在做UIScrollView嵌套时,需要做多手势支持操作
实现 快速滚动时 内外嵌套ScrollView 能够 无缝衔接 滚动操作
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
#import <UIKit/UIKit.h>
//继承自 UIScrollView innerScrollView必须使用此ScrollView
@interface GWScrollView : UIScrollView
@end
#import "GWScrollView.h"
@implementation GWScrollView
// 手势传递给父试图
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
@end
如果是Swift 需要明确显示遵守UIGestureRecognizerDelegate协议 来实现 手势方法,否则无效
extension GWScrollView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true;
}
}
extension GWScrollView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true;
}
}
定义一个类 实例 用户保存 内外 scrollView 的滚动状态,这里不可使用 scrollView.scrollEnabled,因为scrollView.scrollEnabled = NO;时会阻止手势
#import <UIKit/UIKit.h>
@interface SyncScrollContext : NSObject
@property (nonatomic, assign) CGFloat maxOuterOffsetY;
@property (nonatomic, assign) CGFloat maxinnerOffsetY;
@property(nonatomic, assign) BOOL outerEnable;
@property(nonatomic, assign) BOOL innerEnable;
@end
#import <UIKit/UIKit.h>
@interface SyncScrollContext : NSObject
@property (nonatomic, assign) CGFloat maxOuterOffsetY;
@property (nonatomic, assign) CGFloat maxinnerOffsetY;
@property(nonatomic, assign) BOOL outerEnable;
@property(nonatomic, assign) BOOL innerEnable;
@end
#import "SyncScrollContext.h"
@implementation SyncScrollContext
@end
布局试图 outScrollerView 嵌套 innerScrollerView
#import "ScrollViewController.h"
#import "GWScrollView.h"
#import "SyncScrollContext.h"
@interface ScrollViewController ()<UIScrollViewDelegate>
@property(nonatomic, strong)UIScrollView *outScrollerView;
@property(nonatomic, strong)UIScrollView *innerScrollerView;
@property(nonatomic, strong)SyncScrollContext *syncScrollContext;
@end
@implementation ScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.syncScrollContext = [SyncScrollContext new];
self.syncScrollContext.maxinnerOffsetY = 0.f;
self.syncScrollContext.maxOuterOffsetY = 380.f;
self.syncScrollContext.innerEnable = NO;
self.syncScrollContext.outerEnable = YES;
self.outScrollerView.frame = CGRectMake(10, 90, 300, 550);
self.outScrollerView.contentSize = CGSizeMake(300, 1800);
[self.view addSubview:self.outScrollerView];
self.innerScrollerView.frame = CGRectMake(10, 400, 240, 300);
self.innerScrollerView.contentSize = CGSizeMake(240, 800);
[self.outScrollerView addSubview:self.innerScrollerView];
UIView *v1 = [UIView new];
v1.frame = CGRectMake(90, 0, 90, 90);
v1.backgroundColor = UIColor.yellowColor;
[self.innerScrollerView addSubview:v1];
UIView *v2 = [UIView new];
v2.frame = CGRectMake(90, 90, 90, 90);
v2.backgroundColor = UIColor.redColor;
[self.innerScrollerView addSubview:v2];
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.outScrollerView) {
CGPoint contentOffset = scrollView.contentOffset;
if (!self.syncScrollContext.outerEnable) {
[self.outScrollerView setContentOffset:CGPointMake(0, self.syncScrollContext.maxOuterOffsetY)];
}
// 修改 内外scrollView 的滚动状态
if (self.outScrollerView.contentOffset.y >= self.syncScrollContext.maxOuterOffsetY) {
self.syncScrollContext.outerEnable = NO;
self.syncScrollContext.innerEnable = YES;
/*
外层滚动到目标位置后 把滚动距离 传给内存 scrollView
当外层 scrollerView 滚动到最大值后 内部scrollerView 接着滚动剩余部分
*/
CGFloat offsetY = contentOffset.y - self.syncScrollContext.maxOuterOffsetY;
CGPoint innerOffset = self.innerScrollerView.contentOffset;
innerOffset.y += offsetY;
[self.innerScrollerView setContentOffset:innerOffset];
} else {
self.syncScrollContext.outerEnable = YES;
self.syncScrollContext.innerEnable = NO;
}
}
else if (scrollView == self.innerScrollerView) {
if (!self.syncScrollContext.innerEnable) {
[self.innerScrollerView setContentOffset:CGPointMake(0, self.syncScrollContext.maxinnerOffsetY)];
}
// 修改 内外scrollView 的滚动状态
if (self.innerScrollerView.contentOffset.y <= self.syncScrollContext.maxinnerOffsetY) {
self.syncScrollContext.innerEnable = NO;
self.syncScrollContext.outerEnable = YES;
}else {
self.syncScrollContext.innerEnable = YES;
self.syncScrollContext.outerEnable = NO;
}
}
}
- (UIScrollView *)outScrollerView {
if (!_outScrollerView) {
_outScrollerView = [[GWScrollView alloc] init];
_outScrollerView.delegate = self;
_outScrollerView.backgroundColor = UIColor.blueColor;
}
return _outScrollerView;
}
- (UIScrollView *)innerScrollerView {
if (!_innerScrollerView) {
_innerScrollerView = [[GWScrollView alloc] init];
_innerScrollerView.delegate = self;
_innerScrollerView.backgroundColor = UIColor.grayColor;
}
return _innerScrollerView;
}
@end
#import "ScrollViewController.h"
#import "GWScrollView.h"
#import "SyncScrollContext.h"
@interface ScrollViewController ()<UIScrollViewDelegate>
@property(nonatomic, strong)UIScrollView *outScrollerView;
@property(nonatomic, strong)UIScrollView *innerScrollerView;
@property(nonatomic, strong)SyncScrollContext *syncScrollContext;
@end
@implementation ScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.syncScrollContext = [SyncScrollContext new];
self.syncScrollContext.maxinnerOffsetY = 0.f;
self.syncScrollContext.maxOuterOffsetY = 380.f;
self.syncScrollContext.innerEnable = NO;
self.syncScrollContext.outerEnable = YES;
self.outScrollerView.frame = CGRectMake(10, 90, 300, 550);
self.outScrollerView.contentSize = CGSizeMake(300, 1800);
[self.view addSubview:self.outScrollerView];
self.innerScrollerView.frame = CGRectMake(10, 400, 240, 300);
self.innerScrollerView.contentSize = CGSizeMake(240, 800);
[self.outScrollerView addSubview:self.innerScrollerView];
UIView *v1 = [UIView new];
v1.frame = CGRectMake(90, 0, 90, 90);
v1.backgroundColor = UIColor.yellowColor;
[self.innerScrollerView addSubview:v1];
UIView *v2 = [UIView new];
v2.frame = CGRectMake(90, 90, 90, 90);
v2.backgroundColor = UIColor.redColor;
[self.innerScrollerView addSubview:v2];
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.outScrollerView) {
CGPoint contentOffset = scrollView.contentOffset;
if (!self.syncScrollContext.outerEnable) {
[self.outScrollerView setContentOffset:CGPointMake(0, self.syncScrollContext.maxOuterOffsetY)];
}
// 修改 内外scrollView 的滚动状态
if (self.outScrollerView.contentOffset.y >= self.syncScrollContext.maxOuterOffsetY) {
self.syncScrollContext.outerEnable = NO;
self.syncScrollContext.innerEnable = YES;
/*
外层滚动到目标位置后 把滚动距离 传给内存 scrollView
当外层 scrollerView 滚动到最大值后 内部scrollerView 接着滚动剩余部分
*/
CGFloat offsetY = contentOffset.y - self.syncScrollContext.maxOuterOffsetY;
CGPoint innerOffset = self.innerScrollerView.contentOffset;
innerOffset.y += offsetY;
[self.innerScrollerView setContentOffset:innerOffset];
} else {
self.syncScrollContext.outerEnable = YES;
self.syncScrollContext.innerEnable = NO;
}
}
else if (scrollView == self.innerScrollerView) {
if (!self.syncScrollContext.innerEnable) {
[self.innerScrollerView setContentOffset:CGPointMake(0, self.syncScrollContext.maxinnerOffsetY)];
}
// 修改 内外scrollView 的滚动状态
if (self.innerScrollerView.contentOffset.y <= self.syncScrollContext.maxinnerOffsetY) {
self.syncScrollContext.innerEnable = NO;
self.syncScrollContext.outerEnable = YES;
}else {
self.syncScrollContext.innerEnable = YES;
self.syncScrollContext.outerEnable = NO;
}
}
}
- (UIScrollView *)outScrollerView {
if (!_outScrollerView) {
_outScrollerView = [[GWScrollView alloc] init];
_outScrollerView.delegate = self;
_outScrollerView.backgroundColor = UIColor.blueColor;
}
return _outScrollerView;
}
- (UIScrollView *)innerScrollerView {
if (!_innerScrollerView) {
_innerScrollerView = [[GWScrollView alloc] init];
_innerScrollerView.delegate = self;
_innerScrollerView.backgroundColor = UIColor.grayColor;
}
return _innerScrollerView;
}
@end