可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a viewcontroller with 2 CollectionView Controllers.
I would like on rotation that only one of the collection views resize to a custom size while the other remains the same.
I have tried to use:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
// Adjust cell size for orientation
if (UIDeviceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
return CGSizeMake(170.f, 170.f);
}
return CGSizeMake(192.f, 192.f);
}
However that just changes both collectionviews. How do I get it to be specific to only one collection?
回答1:
Heres my 2 cents - because your item sizes are static why don't you set the item size on the collectionViewLayout
?
It will be quicker to do this rather than expecting the collection view to call its delegate method for every cell.
viewWillLayoutSubviews
can be used for detection of rotation on view controllers.
invalidateLayout
can be used on a collection view layout to force it to prepare the layout again causing it to position the elements with the new sizes in this case.
- (void)viewWillLayoutSubviews;
{
[super viewWillLayoutSubviews];
UICollectionViewFlowLayout *flowLayout = (id)self.firstCollectionView.collectionViewLayout;
if (UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication.statusBarOrientation)) {
flowLayout.itemSize = CGSizeMake(170.f, 170.f);
} else {
flowLayout.itemSize = CGSizeMake(192.f, 192.f);
}
[flowLayout invalidateLayout]; //force the elements to get laid out again with the new size
}
edit: updated with Swift2.0 example
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
return
}
if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
flowLayout.itemSize = CGSize(width: 170, height: 170)
} else {
flowLayout.itemSize = CGSize(width: 192, height: 192)
}
flowLayout.invalidateLayout()
}
回答2:
With new API since iOS 8 I'm using:
Swift 3:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
updateCollectionViewLayout(with: size)
}
private func updateCollectionViewLayout(with size: CGSize) {
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.itemSize = (size.width < size.height) ? itemSizeForPortraitMode : itemSizeForLandscapeMode
layout.invalidateLayout()
}
}
Objective-C:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self updateCollectionViewLayoutWithSize:size];
}
- (void)updateCollectionViewLayoutWithSize:(CGSize)size {
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
layout.itemSize = (size.width < size.height) ? itemSizeForPortraitMode : itemSizeForLandscapeMode;
[layout invalidateLayout];
}
回答3:
The Verified Answer Is Not Efficient
The Problem - Invalidating the layout in viewWillLayoutSubviews() is heavy work. viewWillLayoutSubviews() gets called multiple times when a ViewController is instantiated.
My Solution (Swift) - Embed your size manipulation within the UICollectionViewDelegateFlowLayout.
// Keep a local property that we will always update with the latest
// view size.
var updatedSize: CGSize!
// Use the UICollectionViewDelegateFlowLayout to set the size of our
// cells.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// We will set our updateSize value if it's nil.
if updateSize == nil {
// At this point, the correct ViewController frame is set.
self.updateSize = self.view.frame.size
}
// If your collectionView is full screen, you can use the
// frame size to judge whether you're in landscape.
if self.updateSize.width > self.updateSize.height {
return CGSize(width: 170, 170)
} else {
return CGSize(width: 192, 192)
}
}
// Finally, update the size of the updateSize property, every time
// viewWillTransitionToSize is called. Then performBatchUpdates to
// adjust our layout.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
self.updateSize = size
self.collectionView!.performBatchUpdates(nil, completion: nil)
}
回答4:
You can change your if statement. You will need to have a reference to the collection views
if (UIDeviceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) && collectionView == self.firstCollectionView) {
return CGSizeMake(170.f, 170.f);
}
回答5:
The question was , how to separate two collection views in sizeForItemAtIndexPath.
Why just not something like this?
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if collectionView == firstCollectionView{
return CGSize(width: self.frame.width/3, height: 40)
}
else if collectionView == secondCollectionView{
return CGSize(width: self.frame.width, height: self.frame.height-2)
}
abort()
}
回答6:
I've solved this problem by checking the rotation of the device and then reloading the layout. Checking the screen size and using this size to display my cell.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
//get new width of the screen
screenRect = [[UIScreen mainScreen] bounds];
screenWidth = screenRect.size.width;
//if landscape mode
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation))
{
screenDivide = 5;
}
else
{
screenDivide = 3;
}
//reload the collectionview layout after rotating
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
[layout invalidateLayout];
}
Then I defined the cells like this
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
screenRect = [[UIScreen mainScreen] bounds];
screenWidth = screenRect.size.width;
CGSize elementSize = CGSizeMake(screenWidth/screenDivide, 100);
return elementSize;
}
回答7:
I've handled this by detecting the orientation change in the setBounds:
method. We subclass the UICollectionView
and override the setBounds:
method (see below). In flowLayoutForGallerySize
we create a new UICollectionViewFlowLayout
with different spacing and itemSize depending on the size of the collection view.
- (void)setBounds:(CGRect)bounds
{
// detect orientation changes and update itemSize in the flow layout
CGSize oldSize = self.bounds.size;
[super setBounds:bounds];
BOOL wasLandscape = (oldSize.width > oldSize.height);
BOOL nowLandscape = (bounds.size.width > bounds.size.height);
if (wasLandscape != nowLandscape) {
// create a new flow layout object for the new gallery size
self.flowLayout = [self flowLayoutForGallerySize:bounds.size];
// change to the new flow layout
[self setCollectionViewLayout:self.flowLayout animated:NO];
DLog(@"orientation change to %@", NSStringFromCGSize(bounds.size));
}
}