35

It seems like Apple's new feature of auto-flip interface on RTL languages cause problems when using UICollectionView.

I used constraints of type Trailing/Leading for the collection view and they switched their values, as they should, on RTL language.

The problem is that the data actually presented is of the last indexPath in the collection's data source but the UIScrollView.contentOffset.x of the first cell is 0.

A proper behaviour would have been one of the following:

  1. Displaying the first indexPath correctly and switching the direction of the scroll (to the right) - Best option
  2. Not flipping the UI/Constraints so the presented-data / indexPath / scrollView.contentOffset.x will be synchronised - Option that disabling the RTL support.
  3. Presenting cell and data of the last indexPath but fixing the scrollView.contentOffset.x to represent the last cell position also.

I guess Apple might fix it sometime in the future but meanwhile we'll have to use workarounds like reversing array and/or scrolling to the last object.

Paebbels
  • 15,573
  • 13
  • 70
  • 139
MatanGold
  • 739
  • 10
  • 18
  • 1
    Could you please file a bug report at [bugreport.apple.com](http://bugreport.apple.com/)? – wakachamo Oct 16 '15 at 11:23
  • @wakachamo Will do! Thanks for the direct link... – MatanGold Oct 18 '15 at 06:28
  • 3
    If you do implement a workaround please scroll the view instead of reversing the array – the bug is in the initial position, not the layout itself. – lensovet Oct 19 '15 at 10:22
  • Have you had a response from Apple yet? It might also be worth adding this to http://openradar.appspot.com/ to help others track the issue. – Keab42 Oct 26 '15 at 09:00
  • I've been looking at this to come up with a workaround today. The UICollectionview is populated in the right order. So Cell 0 is the top rightmost cell when you're running with RTL languages. The best method I've come up with so far is to use scrollToItemAtIndexPath and giving it item 0 to scroll to. However I'm finding that this isn't 100% reliable. – Keab42 Oct 26 '15 at 15:32
  • @Keab42 as I wrote in the original message - what you did is my first suggested solution and the best for my opinion, but you're right - from some reason it's not 100% consistent. Will update Apple response to the bug report, if I'll get one. – MatanGold Oct 28 '15 at 14:37
  • Have you got any solution on it – swapnil Jul 14 '16 at 06:20
  • Same issue on iOS10. – Timur Bernikovich Jan 20 '17 at 08:58
  • 1
    I have another issue with UICollectionViewFlowLayout and RTL. If I return different sizes for cell, layout is always LTR. :O – Timur Bernikovich Jan 20 '17 at 09:42

5 Answers5

28

I was in a similar situation and found a solution for this. If you are using swift, add the following snippet to your project, and it will make sure that the bounds.origin always follows leading edge of the collection view.

extension UICollectionViewFlowLayout {

    open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
        return true
    }
}

If you are using Objective-C, just subclass the UICollectionViewLayout class, and override flipsHorizontallyInOppositeLayoutDirection, and return true. Use this subclass as the layout object of your collection view.

Suran
  • 1,209
  • 1
  • 17
  • 21
4

I am late but if you don't want to create an extension because it will affect all the collection View in our app. Simply create your own custom class ie.

class CustomLayoutForLocalization : UICollectionViewFlowLayout{
    open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
         return true
    } 
}

To use this class:

// your way of deciding on whether you need to apply this layout may vary depending on use of other tools like LanguageManager_iOS to handle multi-language support
if myCollectionView.effectiveUserInterfaceLayoutDirection == .rightToLeft  {
    let customLayout = CustomLayoutForRTL()
    // if your elements are variable size use the following line
    customLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    // if you want horizontal scroll (single line)
    customLayout.scrollDirection = .horizontal
    myCollectionView.collectionViewLayout = customLayout
}
rounak
  • 9,217
  • 3
  • 42
  • 59
Muhammad Ali
  • 587
  • 5
  • 9
0

There is one common solution for that problem that works for me, follow below steps to overcome that problem,

  • Give the auto layout constraint as per your requirement and then from attribute inspector change the semantic control property of the collection view to Force right-to-left from the storyboard.

enter image description here

  • Then open storyboard as source code and find for the “leading” attributes of your relevant collection view and replace that with the “left” and same for the “trailing” replace that with the “right”. Now you almost done.

enter image description here

enter image description here

enter image description here

  • now that will give you result as per your requirement.
0
import UIKit

extension UICollectionViewFlowLayout {

    open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
        return UIApplication.shared.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.rightToLeft
    }
iTarek
  • 736
  • 1
  • 8
  • 17
0

not pretty though simple math does the trick. (for horizontal collectionview)

- (void)switchSemanticDirection:(UISwitch*)sender {
    //TEST switch the semantic direction between LTR and RTL.
    if (sender.isOn) {
        UIView.appearance.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
    } else {
        UIView.appearance.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
    }

    [self.myContent removeFromSuperview];
    [self.view addSubview:self.myContent];
    
    //reload your collection view to apply RTL setting programmatically
    [self.list reloadData];
    //position your content into the right offset after flipped RTL
    self.list.contentOffset = CGPointMake(self.list.contentSize.width - self.list.contentOffset.x - self.list.bounds.size.width, 
    self.list.contentOffset.y);
}