文章从莫名被封停的简书账号中迁移 2018.04.11 21:20:41 著
老规矩,上图(无图无真相),看官们可以先自行思考一下怎么实现才不显得代码臃肿,易于维护和调试,效果图奉上:
| 原版 | 实现 |
|---|---|
好的....
思考时间结束。
观察现象 如下:第二个 imageView2 初始位置显示位置是在最中间的部分处于屏幕的边缘,scrollView 滚动一屏的宽是全部显示。
实现思路 如下:根据 scrollView 的滚动的范围动态改变 imageView 的位置。
这里会有一个思维误区:就是直接把 imageView 直接加到 scrollView 上,然后去改变 imageView 在 scrollView 上的布局,这样做你还要动态的改变 scrollView 的 contentSize ,并且你在使用 pagingEnabled 属性时也会出现一些意料之外的问题;
要优雅的实现类似的交互效果,我们应当从面向对象的角度思考(oop),imageView 应该被封装出能够单独处理自己偏移的对象,scrollView 仅处理 偏移量 的计算,不应该改变自身的 contentSize (类似于 collectionView 和 collectionCell 的处理逻辑);
项目代码传送门;
好了,接下来我们来一步步实现:
-
使用
storyboard快速创建界面,并添加相应的约束,scrollView设置了背景色方便调试;注意图中约束的条件
-
创建
DRContentView -
将
imageView的需要做偏移的约束设置为DRContentView的属性,并将勾选DRContentViwe的Clip to Bounds,控制偏移的约束如下图所示,leading,trailing -
在
DRContentView.h里暴露一个修改偏移量的方法,并在.m实现它;#import <UIKit/UIKit.h> @interface DRContentView : UIView - (void)setImage:(UIImage *)image; /* * 修改偏移量 */ - (void)changeOffset:(CGFloat)value; @end#import "DRContentView.h" @interface DRContentView () @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *trailingSpace; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *leadingSpace; @end @implementation DRContentView - (void)setImage:(UIImage *)image { self.imageView.image = image; } - (void)changeOffset:(CGFloat)value { self.leadingSpace.constant = -value; self.trailingSpace.constant = value; } @end -
回到
ViewController.m中,添加视图;#import "ViewController.h" #import "DRContentView.h" @interface ViewController ()<UIScrollViewDelegate> @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. if (@available(iOS 11.0, *)) { self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewDecelerationRateNormal; } else { self.automaticallyAdjustsScrollViewInsets = NO; } self.scrollView.delegate = self; [self addSubviews]; } - (void)addSubviews { CGFloat width = [UIScreen mainScreen].bounds.size.width; CGFloat height = [UIScreen mainScreen].bounds.size.height; for (NSInteger i = 0; i < 4; i++) { NSString *imageName = [NSString stringWithFormat:@"%@",@(i+1)]; UIImage *image = [UIImage imageNamed:imageName]; DRContentView *subview = [[[NSBundle mainBundle] loadNibNamed:@"DRContentView" owner:nil options:nil] lastObject]; subview.frame = CGRectMake(i * width, 0, width, height); [subview setImage:image]; [self.scrollView addSubview:subview]; if (i == 0 || i == 3) { continue; } [subview changeOffset:(width * 0.5)]; } self.scrollView.contentSize = CGSizeMake(4 * width, height - 20); self.scrollView.pagingEnabled = YES; self.scrollView.bounces = NO; } #pragma mark --- UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { } @end -
cmd+R效果如下如下; -
我们在
- scrollViewDidScroll:防范重进行对应contentView的偏移量计算;注意:这里设置了
UIScrollView的showsVerticalScrollIndicator和showsHorizontalScrollIndicator方法为NO(sb中取消勾选),否则你就要在区出DRContentView时进行相应的判断了🙈/* 计算公式: 初始偏移量 = (width * 0.5) 滑动偏移量 = (offsetX - width * index) 改变的值 = 初始偏移量 - 滑动偏移量 */ - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat offsetX = scrollView.contentOffset.x; CGFloat width = scrollView.bounds.size.width; if (offsetX < width) { //获取到第二个 item DRContentView *item = scrollView.subviews[1]; [item changeOffset:(width * 0.5 - offsetX * 0.5)]; }else if (offsetX < width * 2) { DRContentView *item2 = scrollView.subviews[1]; [item2 changeOffset:0]; //获取到第三个 item DRContentView *item = scrollView.subviews[2]; [item changeOffset:(width * 0.5 - (offsetX - width) * 0.5)]; }else if (offsetX < width * 3 - 0.1) { DRContentView *item3 = scrollView.subviews[2]; [item3 changeOffset:0]; //获取到第四个 item DRContentView *item = scrollView.subviews[3]; [item changeOffset:(width * 0.5 - ((offsetX - 2 * width) * 0.5))]; }else if (offsetX >= width * 3 - 0.1) { //获取到第四个 item DRContentView *item = scrollView.subviews[3]; [item changeOffset:0]; } }对应的计算问题大家应该都能自己算出来,这里不做过多讨论了,至于其他需要补充的,还是看github源码吧,用到的图片就不单独贴出来
最后来一张 iPhoneX 的截图