UICollectionView - 调整设备上的单元格旋转 - Swift

时间:2015-12-07 11:43:46

标签: swift rotation resize uicollectionview

我已经创建了一个UICollectionView,这样我就可以将视图排列成整齐的列。我希望设备上有一个专栏> 500像素宽。

为了实现这一目标,我创建了这个功能:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    let size = collectionView.frame.width
    if (size > 500) {
        return CGSize(width: (size/2) - 8, height: (size/2) - 8)
    }
    return CGSize(width: size, height: size)
}

这在第一次加载时按预期工作,但是当我旋转设备时,计算不会再次发生,并且视图不会按预期重绘。这是我旋转设备时的代码:

override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
    collectionView.collectionViewLayout.invalidateLayout()
    self.view.setNeedsDisplay()
}

我假设我忘了重绘某些内容,但我不确定是什么。任何想法都非常感激地收到了!

9 个答案:

答案 0 :(得分:59)

也许最直接的方法是在viewWillTransitionToSize期间使invalidateLayout无效:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
        return
    }
    flowLayout.invalidateLayout()
}

答案 1 :(得分:18)

您可以使用viewWillLayoutSubviewsThis question应该会有所帮助,但只要视图控制器视图要布局其子视图,就会以低音方式调用它。

所以你的代码看起来像这样:

override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()

  guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
    return
  }

  if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
    //here you can do the logic for the cell size if phone is in landscape
  } else {
    //logic if not landscape 
  }

  flowLayout.invalidateLayout()
}

答案 2 :(得分:13)

我倾向于使用viewWillTransitionToSize使用相同的内容,只是在那里打了一个电话invalidateLayout()

答案 3 :(得分:2)

也可以使用

traitCollectionDidChange方法代替viewWillLayoutSubviews

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    guard let previousTraitCollection = previousTraitCollection, traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass ||
        traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass else {
            return
    }

    if traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass == .regular {
        // iPad portrait and landscape
        // do something here...
    }
    if traitCollection.horizontalSizeClass == .compact && traitCollection.verticalSizeClass == .regular {
        // iPhone portrait
        // do something here...
    }
    if traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass = .compact {
        // iPhone landscape
        // do something here...
    }
    collectionView?.collectionViewLayout.invalidateLayout()
    collectionView?.reloadData()
}

答案 4 :(得分:2)

我使用了以下方法,对我有用。 我的问题是我使

中的布局无效

viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)

但是在这一点上(如该方法名称所建议的),设备尚未旋转。 因此,此方法将在viewWillLayoutSubviews之前调用,因此在此方法中,我们没有右边界和框架(安全区域),因为设备随后将旋转。

所以我用了通知

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
}

@objc func rotated(){
    guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
        return
    }
    flowLayout.invalidateLayout()
}

,然后在集合视图流委托方法中,一切正常。

extension ViewController: UICollectionViewDelegateFlowLayout{
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
        if #available(iOS 11.0, *) {
            return CGSize(width: view.safeAreaLayoutGuide.layoutFrame.width, height: 70)
        } else {
            return CGSize(width: view.frame.width, height: 70)
        }
    }
}

答案 5 :(得分:1)

要使集合视图调整其单元格的大小并在旋转过程中对更改进行动画处理,请使用过渡协调器:

(快速键4 +)

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    // Have the collection view re-layout its cells.
    coordinator.animate(
        alongsideTransition: { _ in self.collectionView.collectionViewLayout.invalidateLayout() },
        completion: { _ in }
    )
}

答案 6 :(得分:0)

我有一个任务来调整UICollectionView子类中的项目大小。最好的方法是setFrame。我从ViewController传递的属性collectionViewFlowLayout(在我的例子中,它是默认流布局的插座)。

// .h
@property (nonatomic, weak) UICollectionViewFlowLayout *collectionViewFlowLayout;

// .m
- (void)setFrame:(CGRect)frame {
    if (!CGSizeEqualToSize(frame.size, self.frame.size)) {
        collectionViewFlowLayout.itemSize =
        CGSizeMake((UIDeviceOrientationIsLandscape(UIDevice.currentDevice.orientation) ? (frame.size.width - 10) / 2 : frame.size.width), collectionViewFlowLayout.itemSize.height);
    }
    [super setFrame:frame];
}

答案 7 :(得分:0)

您可以创建蒙版内容并将其移动到collectionView。完成横向/纵向动画后,必须尽快将其删除。

以下是一个例子:

@property (strong, nonatomic) UIImageView *maskImage;

.........

- (UIImageView *) imageForCellAtIndex: (NSInteger) index {
    UICollectionView *collectionView = self.pagerView.test;
    FSPagerViewCell *cell = nil;
    NSArray *indexPaths = [collectionView indexPathsForVisibleItems];
    for (NSIndexPath *indexPath in indexPaths) {
        if (indexPath.item == index) {
            cell = (FSPagerViewCell *)[collectionView cellForItemAtIndexPath: indexPath];
            break;
        }
    }
    if (cell) {
        return cell.imageView;
    }
    return nil;
}


- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator

{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
     {
         UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
         [self test_didRotateFromInterfaceOrientation: orientation];

         UIImageView *imageView = [self imageForCellAtIndex: self.pagerView.currentIndex];
         if (imageView) {
                 UIImageView *imageView = [self imageForCellAtIndex: self.pagerView.currentIndex];
                 CGSize itemSize = self.pagerView.itemSize;
                 UIImageView *newImage = [[UIImageView alloc] initWithImage: imageView.image];
                 [newImage setFrame: CGRectMake((_contentView.bounds.size.width - itemSize.width)/2.0f, 0, itemSize.width, itemSize.height)];
                 newImage.contentMode = imageView.contentMode;
                 newImage.clipsToBounds = imageView.clipsToBounds;
                 newImage.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
                 [self.pagerView addSubview: newImage];

                 self.maskImage = newImage;
         }

         [self.pagerView.test performBatchUpdates:^{
             [self.pagerView.test setCollectionViewLayout:self.pagerView.test.collectionViewLayout animated:YES];
         } completion:nil];
         // do whatever
     } completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
     {
         [self.maskImage removeFromSuperview];
     }];
}

答案 8 :(得分:0)

这是针对其他可能具有相同特例的人。 情况: 我在表视图控制器中包括了UICollectionView作为UITableViewCell。行高设置为UITableViewAutomaticDimension以适应集合视图中单元格的数量。尽管在同一表视图中具有动态内容的其他单元格的行为正确,但它在设备旋转时并未正确布局。经过长时间的研究,我找到了一个可行的解决方案:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    [self reloadTableViewSilently];
}

- (void) reloadTableViewSilently {
    dispatch_async(dispatch_get_main_queue(), ^{
// optional: [UIView setAnimationsEnabled:false];
        [self.tableView beginUpdates];
        [self.tableView endUpdates];
// optional: [UIView setAnimationsEnabled:true];
    });
}