编辑:我在底部附加了一个简单的演示项目Dropbox链接
我有一个UI,其顶部为UICollectionView,底部为scrollview。我希望collectionview中的滚动也同步滚动scrollview。我已在scrollview中禁用了用户交互功能,因此只有collectionview才能影响其中的滚动。
出于此测试目的,每个collectionview项目均为150px。
scrollview中的UIViews是屏幕宽度。因此,对于collectionview项目的每个滚动,我需要按屏幕宽度滚动scrollview。为此,我要计算collectionview偏移量已更改的距离,然后将其除以单元格宽度(150),再乘以scrollview的宽度。
我正在尝试实现以下用户界面:
开始:
将collectionview滚动到单元格1:
将collectionview滚动到单元格2:
前几次这一切都很好,但是当我来回滚动集合视图几次到更长的距离(比如说单元格10-> 0-> 10-> 0依此类推)时,scrollview消失了通过“微小”的距离同步。为了说明这一点,请注意滚动视图右侧的第二个UIView中的“黄色”颜色:
我也可以通过NSLogging滚动视图的contentOffset来看到此问题(请注意几次后它如何开始以0.5的速度开始不同步):
2018-11-25 19:24:28.273278-0500 ScrollViewMatchTest[19412:1203912] Finished: 0
2018-11-25 19:24:31.606521-0500 ScrollViewMatchTest[19412:1203912] Finished: 0.5
2018-11-25 19:24:55.173709-0500 ScrollViewMatchTest[19412:1203912] Finished: 1.5
2018-11-25 19:25:03.007528-0500 ScrollViewMatchTest[19412:1203912] Finished: 1.5
2018-11-25 19:25:07.841096-0500 ScrollViewMatchTest[19412:1203912] Finished: 2.5
2018-11-25 19:26:57.634429-0500
我不太确定是什么导致了此问题,我已经尝试了多种方法来解决它,但没有成功。我可以找出一种解决方法(重设偏移量,并在滚动完成时将其恢复同步),但我想知道为什么会导致此不同步问题。
解决方案,方法是重置scrollView的contentOffset以关闭屏幕宽度的倍数:
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
if (scrollView==self.myCollectionView) {
NSLog(@"Finished: %g",self.myScrollView.contentOffset.x);
NSLog(@"Closest: %g",RoundTo(self.myScrollView.contentOffset.x, self.myScrollView.frame.size.width));
[self.myScrollView setContentOffset:CGPointMake(RoundTo(self.myScrollView.contentOffset.x, self.myScrollView.frame.size.width), self.myScrollView.contentOffset.y) animated:YES];
}
}
float RoundTo(float number, float to)
{
if (number >= 0) {
return to * floorf(number / to + 0.5f);
}
else {
return to * ceilf(number / to - 0.5f);
}
}
我还附加了一个简单的演示项目来说明此问题(运行该应用程序,并在顶部的滚动视图中前后滚动几次):https://www.dropbox.com/s/e2bzgo6abq5wmgw/ScrollViewMatchTest.zip?dl=0
代码如下:
#import "ViewController.h"
#define countOfItems 50
@interface ViewController (){
CGFloat previousOffset_Header;
CGFloat previousOffset_Scrollview;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.myCollectionView registerNib:[UINib nibWithNibName:@"MyCell" bundle:nil] forCellWithReuseIdentifier:@"cell"];
[self.myCollectionView reloadData];
for (NSInteger i=0; i<countOfItems; i++) {
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(i*self.myScrollView.frame.size.width, 0, self.myScrollView.frame.size.width, self.myScrollView.frame.size.height)];
myView.backgroundColor=i%2==0?[UIColor blueColor]:[UIColor yellowColor];
[self.myScrollView addSubview:myView];
}
self.myScrollView.contentSize=CGSizeMake(countOfItems*self.myScrollView.frame.size.width, self.myScrollView.frame.size.height);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return countOfItems;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
cell.backgroundColor = indexPath.item%2==0?[UIColor blueColor]:[UIColor yellowColor];
cell.myLabel.text=[NSString stringWithFormat:@"%ld",indexPath.item];
return cell;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
if (scrollView==self.myCollectionView) {
previousOffset_Header = self.myCollectionView.contentOffset.x;
previousOffset_Scrollview = self.myScrollView.contentOffset.x;
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (scrollView==self.myCollectionView) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(scrollViewDidEndScrollingAnimation:) withObject:scrollView afterDelay:0.1 inModes:@[NSRunLoopCommonModes]];
CGFloat offsetToMoveBy = (self.myCollectionView.contentOffset.x-previousOffset_Header)*(self.myScrollView.frame.size.width/150);
previousOffset_Scrollview = previousOffset_Scrollview +offsetToMoveBy;
[self.myScrollView setContentOffset:CGPointMake(previousOffset_Scrollview, self.myScrollView.contentOffset.y) animated:NO];
previousOffset_Header = self.myCollectionView.contentOffset.x;
}
}
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
NSLog(@"Finished: %g",self.myScrollView.contentOffset.x);
}
@end
答案 0 :(得分:0)
请注意,在viewDidLoad
方法myScrollView
中,框架实际上并未设置为真实设备。
因此,在初始化视图时,视图的宽度和高度在您的代码中可能是错误的。
答案 1 :(得分:0)
您应该做一些不同的事情,即计算偏移量而不使用UIScrollViewDelegate
回调scrollViewDidEndDragging:willDecelerate:
和scrollViewDidEndDecelerating:
的方式。我已经重写了相关代码:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (scrollView==self.myCollectionView) {
[self calculateScrollviewOffset:self.myCollectionView];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (scrollView == self.myCollectionView && !decelerate) {
[self calculateEndPosition: self.myCollectionView];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.myCollectionView) {
[self calculateEndPosition: self.myCollectionView];
}
}
- (void) calculateScrollviewOffset:(UICollectionView *) collectionView {
CGFloat cellWidth = 150.0;
CGFloat percentageMoved = collectionView.contentOffset.x / cellWidth;
[self setScrollViewOffset:percentageMoved animated:false];
}
- (void) calculateEndPosition:(UICollectionView*) collectionView {
CGFloat cellWidth = 150.0;
// NOTE: Add 0.5 to play around with the end animation positioning
CGFloat percentageMoved = floor(collectionView.contentOffset.x / cellWidth);
CGFloat collectionViewFixedOffset = (percentageMoved * cellWidth);
[collectionView setContentOffset:CGPointMake(collectionViewFixedOffset, collectionView.contentOffset.y) animated:true];
[self setScrollViewOffset:percentageMoved animated:true];
}
- (void) setScrollViewOffset:(CGFloat) percentageMoved animated:(BOOL) animated {
CGFloat newOffsetX = percentageMoved * self.myScrollView.frame.size.width;
[self.myScrollView setContentOffset:CGPointMake(newOffsetX, self.myScrollView.contentOffset.y) animated: animated];
}