在 UICollectionView 上从右向左对齐

这是一个相当直接的问题,但我还没有找到一个明确的答案,所以(如果我错过了,请纠正我)。

基本上,我的问题是: 有没有可能将 UICollectionView行的内容从右到左而不是从左到右对齐

在我的研究中,我已经看到答案建议子类化 UICollectionViewFlowLayout,但我还没有能够找到一个例子,其中一个是为右对齐创建的。

我的目标是像下面这样设置2个收集视图:

Example

非常感谢您的帮助!

49888 次浏览

对于任何有同样问题的人:

我最终使用了@chinttu-roxen-ramani 推荐的 UICollectionViewRightAlignedLayout 库:

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

或者通过 Interface Builder:

Through interface builder

我最后对库做了一些修改,但总体来说它工作得很好。

您可以通过对集合视图执行转换并反向翻转其内容来获得类似的结果:

首先,在创建 UICollectionView 时,我对它执行了一个水平翻转:

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

然后子类 UICollectionViewCell在这里对 contentView 做同样的水平翻转:

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

对于任何人试图实现右至左布局的 UICollectionView在迅速

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


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

从 iOS9开始,集合视图根据这个 WWDC 视频支持 RTL。因此,不再需要创建 RTL 流布局(除非您已经在使用自定义布局)。

选择: 编辑计划..。 > 选择 > 快跑 > 应用程序语言 > 从右到左伪语言

enter image description here

构建到模拟器时,文本将向右对齐,集合视图将从右向左排序。

enter image description here

不过有个问题。当 contentOffset.x == 0时,集合视图滚动位置是在 Left边(错误)而不是 Right边(正确)。有关详细信息,请参阅此 堆叠物品

一种变通方法是简单地将 First项滚动到 .Left(有一个问题—— .Left实际上位于 引导的边缘) :

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

在我的测试项目中,我的集合视图嵌套在一个表视图单元格中,所以我不能访问 viewDidAppear()。因此,我最终选择了 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)
}
}

要查看它的运行情况,请查看 这个 Git 回购的 RTL 分支。要查看上下文,请参见这个 博客文章及其评论

enter image description here

不需要对集合视图进行任何 X 转换,只需要强制 RTL:

YourCollectionView.semanticContentAttribute = UISemanticContentAttribute.forceRightToLeft

除了陶菲克的回答:

你也可以通过 Interface Builder 设置 UICollectionView 的 Semantic属性:

Storyboard

有关此属性的详细信息: 在这个问题中

适用于 iOS 9 +

  1. 返回 ViewDidLoad ()中的 CollectionView:

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

  2. 返回到 CellForItemAt中的单元格(因为所有的东西都是镜像的) :

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

现在内容在右边,从右边开始滚动。

你可以从 iOS11开始使用:

extension UICollectionViewFlowLayout {


open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}


}

通过改变 都有 flipsHorizontallyInOppositeLayoutDirectiondevelopmentLayoutDirection,我能够实现一个全屏 RTL 滚动,第一个项目是所有的方式向右,并达到最后一个单元格,用户将需要向左滚动。

像这样:

class RTLCollectionViewFlowLayout: UICollectionViewFlowLayout {


override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}


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

以上的答案都不适合我。主要原因是它们中的大多数都不完整。然而,我发现这个解决方案从 这个链接AlexSerdobintsev

首先,您必须导入以下函数

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

然后调用 FinishedLaunching 中的函数

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

好了,搞定了。 下面是应用更改后的文件:

    [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);
}
}

据我所知,所有这些答案都希望从右到左填充集合视图。例如,如果您标记了单元格1、2、3,那么它们将按照顺序3、2、1出现在集合视图中。在我的示例中,我希望单元格以1、2、3的顺序出现(如文本右对齐时行不满)。为此,我创建了一个简单的 UICollectionViewFlow 布局。

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
}


}
extension UICollectionViewFlowLayout {


open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}


}

如果 UICollectionViewFlowLayout不是动态的,UICollectionView已经支持 rtl 方向。

将图像显示的 估计大小更改为 Nothing 将自动更改 CollectionView 的方向。

enter image description here

另一个问题是在重新加载数据后滚动到集合视图的末尾。

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)
}
}
}
}

以上的答案都不适合我。所以我终于解决了这个问题。我使用 RTL 为2种语言(阿拉伯语,法语) 这是我的解决办法

使用此行将集合视图从左向右传输

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

使用此行可使标签适合集合视图单元格内的标签

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

添加下面的扩展对我来说很有用

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