【问题标题】:UICollectionView - resizing cells on device rotate - SwiftUICollectionView - 在设备旋转上调整单元格大小 - Swift
【发布时间】:2016-03-12 00:11:54
【问题描述】:

我创建了一个 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()
}

我假设我忘记重绘某些东西,但我不确定是什么。任何想法都非常感谢!

【问题讨论】:

标签: swift rotation resize uicollectionview


【解决方案1】:

您可以使用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()
}

【讨论】:

  • 使用这种方法会进入一个无限循环。
  • 我不确定@valvoline 是否正确,这会导致无限循环 - 但是它很可能会导致性能问题,因为它使 collectionview 布局失效的次数远远超过了必要的次数。最好使用viewWillTransitionToSize
  • 此代码导致无限循环。它不应该是正确的答案。
  • 使用此方案时应用死循环
  • 这应该被取消标记为正确并替换为viewWillTransitionToSize 方法。
【解决方案2】:

我倾向于将viewWillTransitionToSize 用于相同的事情,并在其中简单地拨打invalidateLayout()

【讨论】:

    【解决方案3】:

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

    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()
    }
    

    【讨论】:

    • 当你的viewcontroller是一个未折叠的splitviewcontroller的detailcontroller时,这个方法不会被调用。
    • 这让我非常接近。
    【解决方案4】:

    我有一个任务来调整 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];
    }
    

    【讨论】:

      【解决方案5】:

      也可以使用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()
      }
      

      【讨论】:

        【解决方案6】:

        您可以创建一个遮罩内容并将其移动到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];
             }];
        }
        

        【讨论】:

          【解决方案7】:

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

          中使布局无效

          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)
                  }
              }
          }
          

          【讨论】:

          • 到目前为止,这是野兽派的做法。谢谢
          • 叫我傻逼。但我多年来一直在编码,我只是喜欢可以在我的项目中拖放的代码。最好的方法!谢谢!
          【解决方案8】:

          要让集合视图调整其单元格的大小,并在旋转期间使更改动画化,请使用过渡协调器:

          (斯威夫特 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 }
              )
          }
          

          【讨论】:

          • 这是唯一让我在集合视图大小更改和单元格居中期间平滑过渡的东西
          【解决方案9】:

          这适用于可能具有相同特殊情况的其他人。 情况: 我在表格视图控制器中包含了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];
              });
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-03-19
            • 2020-06-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-11-24
            • 1970-01-01
            • 2013-02-24
            相关资源
            最近更新 更多