23

我有一个 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

4 回答 4

23

如果您使用自动布局,问题可能是约束尚未设置框架。尝试调用 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;
    }
于 2013-10-01T19:50:15.713 回答
23

您始终可以强制自动布局进行布局。

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    self.view.layoutIfNeeded()
    self.collectionView.scrollToItemAtIndexPath......
}
于 2015-01-30T18:56:11.303 回答
3

bilobatum的答案是正确的!我写这篇文章是因为我没有评论的声誉......:/

我在我的项目中尝试了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];
}

谢谢白头翁的回答!

于 2013-10-07T13:33:07.240 回答
0

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

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

    if ([self.scheduleDate isThisWeek]) [self.calendarLayout performSelector:@selector(scrollToCurrentTime) withObject:nil afterDelay:1];
}
于 2013-11-03T20:25:46.690 回答