问题
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:
Any help is greatly appreciated!
回答1:
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)];
回答2:
Without doing any Xtransform to the collection view, simply forced RTL:
YourCollectionView.semanticContentAttribute = UISemanticContentAttribute.forceRightToLeft
回答3:
In addition to Tawfik's answer:
You can also set UICollectionView's Semantic
property via Interface Builder:
More about this property: in this question
回答4:
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)
回答5:
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.
回答6:
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
}
}
回答7:
you can use this since iOS 11:
extension UICollectionViewFlowLayout {
open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}
}
回答8:
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:
I ended up making a couple modifications to the library, but overall it works great.
回答9:
For iOS 9+
Reverse your collectionView in viewDidLoad() with this :
myCollectionView.transform = CGAffineTransform(scaleX: -1, y: 1)
Reverse-Back your cell (because all things is mirrored) in cellForItemAt with this :
cell.transform = CGAffineTransform(scaleX: -1, y: 1)
Now content is on right side and scroll starts from right.
回答10:
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);
}
}
回答11:
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
}
}
来源:https://stackoverflow.com/questions/25598194/aligning-right-to-left-on-uicollectionview