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 quest
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);
}
}
Without doing any Xtransform to the collection view, simply forced RTL:
YourCollectionView.semanticContentAttribute = UISemanticContentAttribute.forceRightToLeft
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.
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)
}
}
}
}
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
When you build to Simulator, text will be right-aligned, and your Collection View will be ordered from Right to Left.
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.
you can use this since iOS 11:
extension UICollectionViewFlowLayout {
open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}
}
extension UICollectionViewFlowLayout {
open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}
}