UIScrollView 详解
UIScrollView全解
最近看到一篇10年的老帖讨论几年Scroll的半屏滚动效果,正好闲来无事,对Scroll进行了比较详细的整理实现,文章后半部分实现了用基础方法实现的解决方案
基础知识
- (void)createScrollView{
_scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image"]];
imageView.userInteractionEnabled = YES;
//imageview对象添加到scrollview上
[_scrollView addSubview:imageView];
//设置scrollview内容的尺寸
_scrollView.contentSize = imageView.bounds.size;
//设置内容的偏移量,contentOffset参照contentSize的坐标系
_scrollView.contentOffset = CGPointMake(1000, 500);
//设置是否回弹
_scrollView.bounces = YES;
//设置内容的边距
_scrollView.contentInset = UIEdgeInsetsMake(10, 10, 10, 10);
//设置是否可以滚动
_scrollView.scrollEnabled = YES;
//是否可以滚动到内容的顶部(点击状态栏)
_scrollView.scrollsToTop = YES;
//按页滚动
_scrollView.pagingEnabled =YES;
//是否显示水平和垂直方向的指示器
_scrollView.showsHorizontalScrollIndicator = YES;
_scrollView.showsVerticalScrollIndicator = YES;
//设置指示器的样式
_scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
//设置代理
_scrollView.delegate = self;
CGFloat imageWidth = imageView.frame.size.width;
//设置最小和最大缩放比例
_scrollView.minimumZoomScale = WIDTH / imageWidth;
_scrollView.maximumZoomScale = 1.5;
[self.view addSubview:_scrollView];
//给UIImageView添加手势
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageViewTapGesture:)];
tap.numberOfTapsRequired = 2;
[imageView addGestureRecognizer:tap];
}
代理使用
UIScrollView 有自己的代理方法,在代理方法中有相当全面的调用时机,借用代理方法可以制作多种,scrollerView的高级使用
#pragma mark - UIScrollViewDelegate
//只要发生滚动,就会调用该方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
// NSLog(@"scrollview滚动");
} // any offset changes
//只要zoomScale发生变化,就会调用该方法
- (void)scrollViewDidZoom:(UIScrollView *)scrollView{
NSLog(@"发生缩放");
} // any zoom scale changes
// called on start of dragging (may require some time and or distance to move)
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
NSLog(@"开始拖动");
}
// called on finger up if the user dragged. velocity is in points/millisecond. targetContentOffset may be changed to adjust where the scroll view comes to rest
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
NSLog(@"将要结束拖动");
}
// called on finger up if the user dragged. decelerate is true if it will continue moving afterwards
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
NSLog(@"拖动结束");
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
NSLog(@"将要开始减速");
} // called on finger up as we are moving
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
NSLog(@"减速结束");//停止滚动
} // called when scroll view grinds to a halt
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{
NSLog(@"滚动动画结束");
} // called when setContentOffset/scrollRectVisible:animated: finishes. not called if not animating
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
//返回可以进行缩放的子视图
return scrollView.subviews[0];
} // return a view that will be scaled. if delegate returns nil, nothing happens
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view {
NSLog(@"将要开始缩放");
} // called before the scroll view begins zooming its content
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale{
NSLog(@"缩放结束");
NSLog(@"%@", view);
#if 0
//恢复原来大小的时候,可能存在问题
if(scale < 1.0){
CGPoint center = view.center;
center.y = HEIGHT / 2;
view.center = center;
}
#else
if (view.frame.size.width > WIDTH) {
scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}else{
[UIView animateWithDuration:0.5 animations:^{
scrollView.contentInset = UIEdgeInsetsMake((HEIGHT - view.frame.size.height) / 2, 0, 0, 0);
}];
}
#endif
} // scale between minimum and maximum. called after any 'bounce' animations
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView{
//scrollToTop = YES,才会调用该方法
//返回YES,可以滚动到顶部;NO 不可以
return YES;
} // return a yes if you want to scroll to the top. if not defined, assumes YES
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView{
NSLog(@"滚动到顶部");
} // called when scrolling animation finished. may be called immediately if already at top
拓展使用 —— ScrollView制作仿乐乎半屏滚动效果
对于ScrollView的半屏滚动,由于PagEnable打开以后只能根据scrollView本身的高宽进行按页滚动,实现原理主要是调用DidScroll代理方法,对内容进行跟踪滚动,可以实现任意距离跟踪滚动,希望有所帮助
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
rateDistance = 380;
_imagetViewArr = [[NSMutableArray alloc]init];
self.delegate = self;
self.contentOffset = CGPointMake(0, 0);
}
return self;
}
-(void)setImagetViewArr:(NSMutableArray *)imagetViewArr
{
_imagetViewArr = imagetViewArr;
[self creatMainview];
}
-(void)creatMainview
{
self.contentSize = CGSizeMake(self.frame.size.width * _imagetViewArr.count, self.frame.size.height);
moverate = 0;
NSLog(@"%lu",(unsigned long)_imagetViewArr.count);
self.pagingEnabled = YES;
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
[self SetImagerView];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch * evt = [touches anyObject];
CGPoint point = [evt locationInView:self];
CGFloat wide = self.frame.size.width;
CGFloat x1 = self.contentOffset.x + wide;
CGFloat x2 = self.contentOffset.x - wide;
if(point.x - moverate >= self.frame.size.width * 0.79 && point.x < self.frame.size.width * 7)
{
[UIView animateWithDuration:0.2 animations:^{
self.contentOffset = CGPointMake(x1, 0);
[self scrollViewDidScroll:self];
} completion:^(BOOL finished) {
}];
}
else if(point.x - moverate <= self.frame.size.width * 0.21 && point.x > self.frame.size.width*0.25)
{
[UIView animateWithDuration:0.2 animations:^{
self.contentOffset = CGPointMake(x2, 0);
[self scrollViewDidScroll:self];
} completion:^(BOOL finished) {
}];
}
[self scrollViewDidScroll:self];
}
-(void)SetImagerView
{
ImageXarr = [[NSMutableArray alloc]init];
int ii = (int)_imagetViewArr.count;
for (int i = 0 ; i < ii; i++) {
UIImageView * imageView = [[UIImageView alloc]initWithFrame:CGRectMake(self.frame.size.width / _imagetViewArr.count * i, 0, self.frame.size.width * 0.6 , self.frame.size.width * 0.5)];
imageView.center = CGPointMake((self.frame.size.width*0.6)/2 * (2 * i + 1.67) , self.frame.size.height/2.0);
//设置背景
CALayer * layer = [imageView layer];
// 边框
// layer.borderColor = [[UIColor blackColor]CGColor];
// layer.borderWidth = 5;
// 阴影
layer.shadowColor = [[UIColor blackColor]CGColor];
layer.shadowOffset = CGSizeMake(10, 5);
layer.shadowRadius = 10;
layer.shadowOpacity = 0.5;
// layer.cornerRadius = 40;
// layer.masksToBounds = YES;
imageView.backgroundColor = [UIColor redColor];
[imageView sd_setImageWithPreviousCachedImageWithURL:_imagetViewArr[i] placeholderImage:[UIImage imageNamed:@"arrow"] options:2 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// float reat = image.size.height / image.size.width;
// imageView.frame = CGRectMake(imageView.frame.origin.x, imageView.frame.origin.y, imageView.frame.size.width, imageView.frame.size.height * reat);
}];
imageView.tag = i + 100;
//[_imagetViewArr addObject:imageView];
[self addSubview:imageView];
};
[self scrollViewDidScroll:self];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat rate;
CGFloat reduce;
reduce = moverate - scrollView.contentOffset.x;
moveNum += reduce;
//NSLog(@"%f",reduce);
for (int i = 0 ; i < _imagetViewArr.count; i++) {
UIView * view = [self viewWithTag:100 + i];
CGFloat distance = fabs(scrollView.contentOffset.x + self.frame.size.width/2 - view.center.x);
CGFloat ratex = view.center.x - reduce * 0.4;
view.center = CGPointMake(ratex,view.center.y);
if (distance >= rateDistance)
rate = 0.7;
else
rate = (rateDistance - distance*0.2) / (rateDistance ) ;
view.transform = CGAffineTransformMakeScale(rate, rate);
}
moverate = scrollView.contentOffset.x;
}