MQ. MQ. - 7 months ago 435
Swift Question

Align cell in center(horizontally) in UICollectionView

I am first time working with UICollectionView not sure how to do this. I am trying to create an app for tvOS and want to display the menu like airbnb tvos app. I have somehow tried to achieve that particular format

didUpdateFocusInContext
but the problem is about the first appearance because the first appearance takes place on default points i.e 0,0 of collection view which results in a mess.
Heres my code what 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 which I want to achieve and I have achieved it but for later indexes means index after 1,2enter image description here

This is the starting behaviour 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 actually want, but i am struggling in getting my focused cell to the middle of the screen and similarly my previous and next on the either sides . I know i am giving frames coordinates explicitly which isn't correct this was just a test case i was running and nothing else but i couldn't find the way to do this

Answer

I'd control the focus and the collection content offset (scroll position) separately.

For the content offset you should set the section margins and the inter-item spacing so that you have one cell centred and the adjacent cells visible at the edges. You can get this setup and tested without showing any focus.

Presumably there is difficulty getting the item to move exactly to the centre when you scroll (the focus changes). To resolve this, implement - scrollViewWillEndDragging:withVelocity:targetContentOffset: to find the item at the targetContentOffset and get its centre point (from the layout attributes). With that you can modify the targetContentOffset such that the scroll ends exactly with the item centred.

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

Note that the below code also applies a motion effect transform similar to the stock supplied by apple when a UIImageView has focus and has adjustsImageWhenAncestorFocused set. If you don't want that you can simplify and shorten the code quite 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)
        }
    }
}
Comments