首页 文章

如果在viewWillAppear中调用scrollToItemAtIndexPath,则在UICollectionView上未更新contentOffset

提问于
浏览
22

我有一个UICollectionView用于模拟iOS 7中的新日历 . 此集合视图位于具有selectedDate属性的控制器内 . 每当设置selectedDate属性时,集合视图应滚动到集合视图中的日期 .

日历控制器的viewWillAppear还确保所选日期可见,因为此控制器已缓存并重复使用 .

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.calendarView scrollToDate:[self selectedDate] animated:NO];
}

问题是,第一次显示日历控制器时滚动不起作用 . 集合视图的contentOffset未更新 .

我目前的解决方法是使用安排滚动在下一个运行循环中进行

dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void)
{
        // Scroll to the date.
    });

看起来当UICollectionView不在窗口中时,您无法滚动 . 将滚动调度到下一个运行循环,确保视图已添加到窗口并可以正确滚动 .

还有其他人遇到过这个问题以及他们的解决方法吗?

4 回答

  • 0

    如果您使用自动布局,问题可能是约束尚未设置帧 . 尝试在viewDidLayoutSubviews中调用scrollToDate:方法(不带dispatch_after) .

    @interface CustomViewController ()
    
    @property (nonatomic) BOOL isFirstTimeViewDidLayoutSubviews; // variable name could be re-factored
    
    @property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
    
    @end
    
    @implementation CustomViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        self.isFirstTimeViewDidLayoutSubviews = YES;
    }
    
    - (void)viewDidLayoutSubviews
    {
        // only after layoutSubviews executes for subviews, do constraints and frames agree (WWDC 2012 video "Best Practices for Mastering Auto Layout")
    
        if (self.isFirstTimeViewDidLayoutSubviews) {
    
            // execute geometry-related code...
    
            // good place to set scroll view's content offset, if its subviews are added dynamically (in code)
    
            self.isFirstTimeViewDidLayoutSubviews = NO;
        }
    
  • 22

    您可以随时强制自动布局布局 .

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.view.layoutIfNeeded()
        self.collectionView.scrollToItemAtIndexPath......
    }
    
  • 3

    bilobatum 's answer is correct! I'我这样写是因为我没有评论的声誉......:/

    我在我的项目中尝试了 bilobatum 的答案,它完美无缺!我的代码:

    -(void)viewDidLayoutSubviews{
        [super viewDidLayoutSubviews];
        if (currentOffset.y != 999) {
            [collectionView setContentOffset:currentOffset animated:NO];
        }
    }
    

    currentOsset是 CGPoint 初始化为x = 0和y = 999值( CGPoint currentOffset = {0,999};

    viewWillDisappear 方法中,我将collectionView的contentOffset保存在currentOffset中 . 这样,如果我导航到具有collectionView的控制器,我之前导航到那里,我将始终拥有最后的位置 .

    适合您的代码:

    -(void)viewDidLayoutSubviews{
        [super viewDidLayoutSubviews];
        [self.calendarView scrollToDate:[self selectedDate] animated:NO];
    }
    

    谢谢 bilobatum 的答案!

  • 18

    使用-viewDidLayoutSubviews创建了一个无限循环,使得解决方案过于复杂 .
    相反,我只是添加了一个小延迟,以便在滚动之前创建约束:

    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
    
        if ([self.scheduleDate isThisWeek]) [self.calendarLayout performSelector:@selector(scrollToCurrentTime) withObject:nil afterDelay:1];
    }
    

相关问题