Aligning right to left on UICollectionView

IosObjective CUicollectionviewRight to-LeftUicollectionviewlayout

Ios Problem Overview


this is a pretty straightforward question, but I haven't been able to find a definitive answer to it on SO (if I missed it, please correct me).

Basically, my question is: Is it possible to align UICollectionView row contents from right to left instead of from left to right?

In my research I've seen answers suggesting subclassing UICollectionViewFlowLayout, but I haven't been able to find an example where one was created for right-alignment.

My goal is to have 2 collection views set up like this:

Example

Any help is greatly appreciated!

Ios Solutions


Solution 1 - Ios

Without doing any Xtransform to the collection view, simply forced RTL:

YourCollectionView.semanticContentAttribute = UISemanticContentAttribute.forceRightToLeft

Solution 2 - Ios

You can get similar result by performing a transform on the collection view and reverse the flip on its content:

First when creating the UICollectionView I performed a horizontal flip on it:

[collectionView_ setTransform:CGAffineTransformMakeScale(-1, 1)];

Then subclass UICollectionViewCell and in here do the same horizontal flip on its contentView:

[self.contentView setTransform:CGAffineTransformMakeScale(-1, 1)];

Solution 3 - Ios

In addition to Tawfik's answer:

You can also set UICollectionView's Semantic property via Interface Builder:

Storyboard

More about this property: in this question

Solution 4 - Ios

By changing both flipsHorizontallyInOppositeLayoutDirection and developmentLayoutDirection, I was able to achieve a full screen RTL scroll where the first item was all the way to the right, and to reach the last cell, user will need to scroll to the left.

Like so:

class RTLCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
    override var flipsHorizontallyInOppositeLayoutDirection: Bool {
        return true
    }

    override var developmentLayoutDirection: UIUserInterfaceLayoutDirection {
        return UIUserInterfaceLayoutDirection.rightToLeft
    }
}

Solution 5 - Ios

you can use this since iOS 11:

extension UICollectionViewFlowLayout {

    open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
        return true
    }

}

Solution 6 - Ios

For anybody trying to achieve Right to Left layout of UICollectionView in Swift

//in viewDidLoad
    YourCollectionView.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)

//in cellForItemAtIndexPath
    cell.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)

Solution 7 - Ios

As of iOS 9, Collection Views support RTL according to this WWDC video. So it's no longer necessary to create an RTL flow layout (unless you're already using a custom layout).

Select: Edit Scheme... > Options > Run > Application Language > Right to Left Pseudolanguage

enter image description here

When you build to Simulator, text will be right-aligned, and your Collection View will be ordered from Right to Left.

enter image description here

There's a problem though. When contentOffset.x == 0, the Collection View scroll position is at the Left edge (wrong) instead of the Right edge (correct). See this stack article for details.

One workaround is to simply scroll the First item to the .Left (There's a gotcha -- .Left is actually on the Right, or Leading edge):

override func viewDidAppear(animated: Bool) {
    if collectionView?.numberOfItemsInSection(0) > 0  {
        let indexPath = NSIndexPath(forItem: 0, inSection: 0)
        collectionView?.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Left, animated: false)
    }
}

In my test project, my Collection View was nested inside a Table View Cell, so I didn't have access to viewDidAppear(). So instead, I ended up hooking into drawRect():

class CategoryRow : UITableViewCell {
    @IBOutlet weak var collectionView: UICollectionView!
    
    override func drawRect(rect: CGRect) {
        super.drawRect(rect)
        scrollToBeginning()
    }
    
    override func prepareForReuse() {
        scrollToBeginning()
    }
    
    func scrollToBeginning() {
        guard collectionView.numberOfItems(inSection: 0) > 0 else { return }
        let indexPath = IndexPath(item: 0, section: 0)
        collectionView.scrollToItem(at: indexPath, at: .left, animated: false)
    }
}

To see this in action, check out the RTL branch on this git repo. And for context, see this blog post and its comments.

enter image description here

Solution 8 - Ios

extension UICollectionViewFlowLayout {

    open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
        return true
    }

}

Solution 9 - Ios

For anyone who has the same question:

I ended up using the UICollectionViewRightAlignedLayout library that @chinttu-roxen-ramani recommended. You can either set it with code:

self.collectionView.collectionViewLayout = [[UICollectionViewRightAlignedLayout alloc] init];

or through interface builder:

Through interface builder

I ended up making a couple modifications to the library, but overall it works great.

Solution 10 - Ios

For iOS 9+

  1. Reverse your collectionView in viewDidLoad() with this :

    myCollectionView.transform = CGAffineTransform(scaleX: -1, y: 1)

  2. Reverse-Back your cell (because all things are mirrored) in cellForItemAt with this :

    cell.transform = CGAffineTransform(scaleX: -1, y: 1)

Now content is on right side and scroll starts from right.

Solution 11 - Ios

Adding below extension worked for me

extension UICollectionViewFlowLayout {
    open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
        return true  //RETURN true if collection view needs to enable RTL
    }
}

Solution 12 - Ios

Non of the above answers worked for me. The main reason was that most of them are not complete. However, I found this solution from this link by AlexSerdobintsev.

In AppDelegate.cs. First, you have to import the following function

[DllImport(ObjCRuntime.Constants.ObjectiveCLibrary, EntryPoint = "objc_msgSend")]
internal extern static IntPtr IntPtr_objc_msgSend(IntPtr receiver, IntPtr selector, UISemanticContentAttribute arg1);

Then call the function inside FinishedLaunching

        var selector = new ObjCRuntime.Selector("setSemanticContentAttribute:");
        IntPtr_objc_msgSend(UIView.Appearance.Handle, selector.Handle, UISemanticContentAttribute.ForceRightToLeft);

voila! we are done. Here is the file after applying the changes:

    [Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
    [DllImport(ObjCRuntime.Constants.ObjectiveCLibrary, EntryPoint = "objc_msgSend")]
    internal extern static IntPtr IntPtr_objc_msgSend(IntPtr receiver, IntPtr selector, UISemanticContentAttribute arg1);


    //
    // This method is invoked when the application has loaded and is ready to run. In this 
    // method you should instantiate the window, load the UI into it and then make the window
    // visible.
    //
    // You have 17 seconds to return from this method, or iOS will terminate your application.
    //
    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        global::Xamarin.Forms.Forms.SetFlags("Shell_Experimental", "Visual_Experimental", "CollectionView_Experimental", "FastRenderers_Experimental");
        global::Xamarin.Forms.Forms.Init();
        LoadApplication(new App());

        ...

        var selector = new ObjCRuntime.Selector("setSemanticContentAttribute:");
        IntPtr_objc_msgSend(UIView.Appearance.Handle, selector.Handle, UISemanticContentAttribute.ForceRightToLeft);

        return base.FinishedLaunching(app, options);
    }
}

Solution 13 - Ios

From what I can tell, all of these answers want to fill the collection view from right to left. For example, if you labeled cells 1, 2, 3, then they would appear in the order 3, 2, 1 in the collection view. In my case I wanted the cells to appear in the order 1, 2, 3 (like text right alignment when the line is not full). In order to do this I created a simple UICollectionViewFlow layout.

import UIKit

class RightAlignFlowLayout: UICollectionViewFlowLayout {
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
    {
        guard let attrsArr = super.layoutAttributesForElements(in: rect) else {
            return nil
        }
        guard let collectionView = self.collectionView else { return attrsArr }
        if self.collectionViewContentSize.width > collectionView.bounds.width {
            return attrsArr
        }
        
        let remainingSpace = collectionView.bounds.width - self.collectionViewContentSize.width
        
        for attr in attrsArr {
            attr.frame.origin.x += remainingSpace
        }
        
        return attrsArr
    }
    
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        guard let attrs = super.layoutAttributesForItem(at: indexPath) else { return nil }
        guard let collectionView = self.collectionView else { return attrs }
        if self.collectionViewContentSize.width > collectionView.bounds.width {
            return attrs
        }
        
        let remainingSpace = collectionView.bounds.width - self.collectionViewContentSize.width
        attrs.frame.origin.x += remainingSpace
        return attrs
    }

}

Solution 14 - Ios

UICollectionView already support rtl direction if UICollectionViewFlowLayout is not dynamic.

Changing Estimate size to None as the image shows automatically changed the direction of the CollectionView.

enter image description here

One more issue is to scroll to the end of the collection view after reloading data.

extension UICollectionView {
    
    func scrollToEndIfArabic() {
        if Language.shared.isArabic() {
            DispatchQueue.main.async {
                self.contentOffset
                    = CGPoint(x: self.contentSize.width
                        - self.frame.width
                        + self.contentInset.right, y: 0)
            }
        }
    }
}

Solution 15 - Ios

None of the above answers working for me. So finally I fixed my issue with this. I am using RTL for 2 languages(Arabic, French) Here is my solution

> Use this line for transferring collection view left to right

let LangCheck = PPLocalization.sharedInstance.getLanguage(forAPI: true)
if(LangCheck == "ar"){
   CollectionView.transform = CGAffineTransform(scaleX: -1, y: 1);
}

> Use this line to make fit your label inside your collection view cell

let LangCheck = PPLocalization.sharedInstance.getLanguage(forAPI: true)
  if(LangCheck == "ar"){
     cell.lbCategoryName.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
  }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionrebello95View Question on Stackoverflow
Solution 1 - IosTawfik BouabidView Answer on Stackoverflow
Solution 2 - IosLK__View Answer on Stackoverflow
Solution 3 - IosmedvedNickView Answer on Stackoverflow
Solution 4 - Ioscohen72View Answer on Stackoverflow
Solution 5 - IosMarwan AlqadiView Answer on Stackoverflow
Solution 6 - IosSaqib OmerView Answer on Stackoverflow
Solution 7 - IosRobert ChenView Answer on Stackoverflow
Solution 8 - Iosurvashi bhagatView Answer on Stackoverflow
Solution 9 - Iosrebello95View Answer on Stackoverflow
Solution 10 - IosSabrinaView Answer on Stackoverflow
Solution 11 - IosKrishna KiranaView Answer on Stackoverflow
Solution 12 - IosAshiView Answer on Stackoverflow
Solution 13 - IosSamBView Answer on Stackoverflow
Solution 14 - IoshasanView Answer on Stackoverflow
Solution 15 - IosMd. ShofiullaView Answer on Stackoverflow