Align cell in center (horizontal) in UICollectionView

This is my first time working with a UICollectionView, not sure how to do this. I am trying to create a tvOS application and want to display a menu, for example, an airbnb tvos application. I somehow tried to achieve this particular format didUpdateFocusInContext , but the problem is in the first appearance, because the first appearance occurs at the default points, i.e. 0.0 from the collection, resulting in a mess. Here is my code that I have done so far.

 func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { if let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as? ShowCell { menuData = dataArray[indexPath.row] as! NSDictionary cell.configureCell(menuData.objectForKey("name") as! String) return cell } else { return ShowCell() } } override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) { if let previousItem = context.previouslyFocusedView as? ShowCell { UIView.animateWithDuration(0.2, animations: { () -> Void in previousItem.showImg.frame.size = self.originalCellSize }) } if let nextItem = context.nextFocusedView as? ShowCell { UIView.animateWithDuration(0.2, animations: { () -> Void in nextItem.showImg.frame = CGRectMake( self.view.frame.width/2 - self.focusCellSize.width/2, 169.0, self.focusCellSize.width, self.focusCellSize.height) }) } } 

This is the view that I want to achieve, and I have achieved it, but for subsequent indices it means the index after 1.2 enter image description here

This is the initial behavior when it appears for the first time, and when I move the control to it, it happens like this: enter image description here

enter image description here

This is what I really want, but I'm struggling to get my concentrated cell in the middle of the screen and similar to my previous and next on both sides. I know that I give the coordinates of the frames explicitly, which is wrong, it was just a test script that I ran, and nothing else but I could not find a way to do this

+7
ios center tvos swift uicollectionview
source share
4 answers

I would control the focus and offset of the contents of the collection (scroll position) separately.

To offset the content, you must set the section margins and the spacing between points so that you have one cell with the center and adjacent cells visible at the edges. You can get this setting and test without any focus.

It is presumably difficult to get an element to move exactly to the center when scrolling (changing focus). To solve this problem, do - scrollViewWillEndDragging:withVelocity:targetContentOffset: to find the element in targetContentOffset and get its center point (from the layout attributes). With this, you can change targetContentOffset so that the scroll ends exactly with the centered element.

Now the focus should be controlled by the cell itself, and not by the collection view. Below is a (slightly larger) example of an animation of changing the focus of a cell. It uses mapping to change the limitations of the image and applies the transform to the label. You can do something similar depending on how you want the image and label to interact with each other.

Please note that the code below also applies a motion effect transformation similar to the stock supplied by the apple when the UIImageView has focus and has adjustsImageWhenAncestorFocused . If you don’t want you to simplify and shorten the code a bit.

 override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) { super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator) if (context.nextFocusedView == self) { UIView.animateWithDuration(0.1, animations: { () -> Void in self.imageConstraints = constrain(self.itemImageView, replace: self.imageConstraints!) { $0.top == $0.superview!.top $0.bottom == $0.superview!.bottom $0.leading == $0.superview!.leading $0.trailing == $0.superview!.trailing } self.itemLabel.transform = CGAffineTransformMakeTranslation(0, 60) self.itemLabel.layer.backgroundColor = UIColor.darkGrayColor().colorWithAlphaComponent(0).CGColor self.layer.shadowOpacity = 1 self.layoutIfNeeded() }, completion: nil) let minMaxAngle = 10.0 let m34 = CGFloat(1.0 / -1250) let angle = CGFloat(minMaxAngle * M_PI / 180.0) var baseTransform = CATransform3DIdentity baseTransform.m34 = m34 let rotateXmin = CATransform3DRotate(baseTransform, -1 * angle, 1.0, 0.0, 0.0); let rotateXmax = CATransform3DRotate(baseTransform, angle, 1.0, 0.0, 0.0); let rotateYmin = CATransform3DRotate(baseTransform, angle, 0.0, 1.0, 0.0); let rotateYmax = CATransform3DRotate(baseTransform, -1 * angle, 0.0, 1.0, 0.0); let verticalMotionEffect = UIInterpolatingMotionEffect(keyPath: "layer.transform", type: .TiltAlongVerticalAxis) verticalMotionEffect.minimumRelativeValue = NSValue(CATransform3D: rotateXmin) verticalMotionEffect.maximumRelativeValue = NSValue(CATransform3D: rotateXmax) let horizontalMotionEffect = UIInterpolatingMotionEffect(keyPath: "layer.transform", type: .TiltAlongHorizontalAxis) horizontalMotionEffect.minimumRelativeValue = NSValue(CATransform3D: rotateYmin) horizontalMotionEffect.maximumRelativeValue = NSValue(CATransform3D: rotateYmax) let group = UIMotionEffectGroup() group.motionEffects = [horizontalMotionEffect, verticalMotionEffect] self.addMotionEffect(group) } else { UIView.animateWithDuration(0.3, animations: { () -> Void in self.imageConstraints = constrain(self.itemImageView, replace: self.imageConstraints!) { $0.top == $0.superview!.top + 20 $0.bottom == $0.superview!.bottom - 20 $0.leading == $0.superview!.leading + 20 $0.trailing == $0.superview!.trailing - 20 } self.itemLabel.transform = CGAffineTransformIdentity self.itemLabel.layer.backgroundColor = UIColor.darkGrayColor().colorWithAlphaComponent(0.75).CGColor self.layer.shadowOpacity = 0 self.layoutIfNeeded() }, completion: nil) for effect in self.motionEffects { self.removeMotionEffect(effect) } } } 
+3
source share

@Wain has already put in a guide to achieve the mentioned style here is my right answer to this question:

 override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) { if let focusedView = context.nextFocusedView as? ShowCell { collectionView.scrollEnabled = false let indexPath = collectionView.indexPathForCell(focusedView)! focusedView.showImg.frame.size = self.focusImageSize focusedView.showLbl.frame.size = self.focusLabelSize focusedView.showLbl.font = UIFont.systemFontOfSize(75) collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .CenteredHorizontally, animated: true) self.collectionView.layoutIfNeeded() } else if let lastFocuedView = context.previouslyFocusedView as? ShowCell{ collectionView.scrollEnabled = true // let indexPath = collectionView.indexPathForCell(lastFocuedView)! lastFocuedView.showImg.frame.size = self.originalImageSize lastFocuedView.showLbl.frame.size = self.originalLabelSize lastFocuedView.showLbl.font = UIFont.systemFontOfSize(70) self.collectionView.layoutIfNeeded() } 

and for the first and last cell you should give UIEdgeInsets (this is the delegate allocation method)

 func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets{ return UIEdgeInsets(top: 0.0, left: self.collectionView.frame.width/2 , bottom: 0.0, right: self.collectionView.frame.width/2) } 

We sincerely thank Wen for giving accurate guidance on this. Hooray!

0
source share

The solution is to override the delegate method of the scroll scrollViewWillEndDragging here fooobar.com/questions/65867 / ... (Swift 3)

0
source share

This method should be located horizontally in any kind of collection, even with additional elements in Swift 4.0 without any changes:

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout let cellWidth: CGFloat = flowLayout.itemSize.width let cellSpacing: CGFloat = flowLayout.minimumInteritemSpacing let cellCount = CGFloat(collectionView.numberOfItems(inSection: section)) var collectionWidth = collectionView.frame.size.width if #available(iOS 11.0, *) { collectionWidth -= collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right } let totalWidth = cellWidth * cellCount + cellSpacing * (cellCount - 1) if totalWidth <= collectionWidth { let edgeInset = (collectionWidth - totalWidth) / 2 return UIEdgeInsetsMake(flowLayout.sectionInset.top, edgeInset, flowLayout.sectionInset.bottom, edgeInset) } else { return flowLayout.sectionInset } } 

Remember that you are not subclassing the UICollectionViewController , make sure your class complies with the UICollectionViewDelegateFlowLayout protocol

0
source share

All Articles