UICollectionView 不会在 RTL 中从右到左填充数据
UICollectionView doesn't fill data from right to left in RTL
我有一个 UICollectionView
并且想要水平显示单元格,例如
英文,应该显示:
|细胞A |细胞B | cellC |
在阿拉伯语中,它应该显示:
[细胞B |细胞A |
对于 RTL,如果每个单元格具有相同的大小,UICollectionViewFlowLayout
默认情况下对我来说工作正常。但是,如果我实现 collectionView:layout:sizeForItemAtIndexPath:
并为每个单元格设置不同的宽度,则 CollectionView 变为:
|细胞A |细胞B |细胞 C |
有什么办法可以解决这个问题吗?
这个 class 对我有用,可以使用不同大小的单元格从左到右显示集合视图。
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
static const char kBundleKey = 0;
@interface BundleEx : NSBundle
@end
@implementation BundleEx
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
NSBundle *bundle = objc_getAssociatedObject(self, &kBundleKey);
if (bundle) {
return [bundle localizedStringForKey:key value:value table:tableName];
}
else {
return [super localizedStringForKey:key value:value table:tableName];
}
}
@end
@implementation NSBundle (Language)
+ (void)setLanguage:(NSString *)language
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
object_setClass([NSBundle mainBundle], [BundleEx class]);
});
if (isCurrentLanguageRTL) {
if ([[[UIView alloc] init] respondsToSelector:@selector(setSemanticContentAttribute:)]) {
[[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceRightToLeft];
[[UITableView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceRightToLeft];
}
}else {
//FOR LTR
if ([[[UIView alloc] init] respondsToSelector:@selector(setSemanticContentAttribute:)]) {
[[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
[[UITableView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
}
}
[[NSUserDefaults standardUserDefaults] synchronize];
id value = language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil;
objc_setAssociatedObject([NSBundle mainBundle], &kBundleKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
希望对您有所帮助。如果需要进一步的帮助,请告诉我。
您需要创建自己的 UICollectionViewLayout
,正如我在评论中所说,我们将从倒着开始,首先您需要在 ViewController
viewDidLoad
方法中添加此行
let semanticLayout = SemanticLayout()
semanticLayout.delegate = self
self.collectionView.collectionViewLayout = semanticLayout
这是您需要实现的委托
extension ViewController: SemanticLayoutDelegate{
func semanticDisposition() -> SemanticDisposition {
return SemanticDisposition.rigthToLeft
}
}
使用您的 ViewController 姓名代替 ViewController ofc
这里有 SemanticLayout
class,检查是否 完全可定制 您可以定义 UICollectionView
是 RTL 还是具有委托方法的 LTR semanticDisposition
import UIKit
protocol SemanticLayoutDelegate: UICollectionViewDelegateFlowLayout {
func semanticDisposition() -> SemanticDisposition
}
enum SemanticDisposition {
case leftToRigth
case rigthToLeft
}
class SemanticLayout: UICollectionViewLayout {
weak var delegate: SemanticLayoutDelegate!
fileprivate var cellPadding: CGFloat = 10
fileprivate var cache = [UICollectionViewLayoutAttributes]()
fileprivate var contentHeight: CGFloat = 0
private var rowsWidth : [CGFloat] = [0]
private var avaiableSpaces : [(Int,CGFloat)] = []
private var currentRow : Int = 0
private var rowHeigths : CGFloat = -1.0
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
private func availableWidthForRow(index:Int) -> CGFloat {
let ocuppedWidth = self.rowsWidth[index]
return self.contentWidth - ocuppedWidth
}
private func canAddCellAtRow(rowIndex:Int,size:CGSize) ->Bool
{
if(availableWidthForRow(index: rowIndex) >= size.width) {
return true
}
return false
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
// Check if cache is empty
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
let viewSize: CGSize = delegate.collectionView!(collectionView, layout: self, sizeForItemAt: indexPath)
if(self.rowHeigths < 0) {
self.rowHeigths = viewSize.height
}
let width = viewSize.width
let height = viewSize.height
var xOffset = self.rowsWidth[self.currentRow]
if(self.canAddCellAtRow(rowIndex: self.currentRow, size: viewSize)) {
if(self.delegate.semanticDisposition() == .rigthToLeft) {
xOffset = (contentWidth - self.rowsWidth[self.currentRow]) - width
}
} else {
self.currentRow += 1
self.rowsWidth.append(0.0)
xOffset = self.rowsWidth[self.currentRow]
if(self.delegate.semanticDisposition() == .rigthToLeft) {
xOffset = (contentWidth - self.rowsWidth[self.currentRow]) - width
}
}
let yOffset = CGFloat(self.currentRow) * self.rowHeigths
let frame = CGRect(x: xOffset, y: yOffset, width: width, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
contentHeight = max(contentHeight, frame.maxY)
self.rowsWidth[self.currentRow] += width
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}
结果
RTL
LTR
class RTLSupportedCollectionViewFlowLayout: UICollectionViewFlowLayout {
override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}
}
您可以使用这种流式布局。仅当所有单元格返回的项目大小不同时才需要它。
我有一个 UICollectionView
并且想要水平显示单元格,例如
英文,应该显示: |细胞A |细胞B | cellC |
在阿拉伯语中,它应该显示: [细胞B |细胞A |
对于 RTL,如果每个单元格具有相同的大小,UICollectionViewFlowLayout
默认情况下对我来说工作正常。但是,如果我实现 collectionView:layout:sizeForItemAtIndexPath:
并为每个单元格设置不同的宽度,则 CollectionView 变为:
|细胞A |细胞B |细胞 C |
有什么办法可以解决这个问题吗?
这个 class 对我有用,可以使用不同大小的单元格从左到右显示集合视图。
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
static const char kBundleKey = 0;
@interface BundleEx : NSBundle
@end
@implementation BundleEx
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
NSBundle *bundle = objc_getAssociatedObject(self, &kBundleKey);
if (bundle) {
return [bundle localizedStringForKey:key value:value table:tableName];
}
else {
return [super localizedStringForKey:key value:value table:tableName];
}
}
@end
@implementation NSBundle (Language)
+ (void)setLanguage:(NSString *)language
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
object_setClass([NSBundle mainBundle], [BundleEx class]);
});
if (isCurrentLanguageRTL) {
if ([[[UIView alloc] init] respondsToSelector:@selector(setSemanticContentAttribute:)]) {
[[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceRightToLeft];
[[UITableView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceRightToLeft];
}
}else {
//FOR LTR
if ([[[UIView alloc] init] respondsToSelector:@selector(setSemanticContentAttribute:)]) {
[[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
[[UITableView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
}
}
[[NSUserDefaults standardUserDefaults] synchronize];
id value = language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil;
objc_setAssociatedObject([NSBundle mainBundle], &kBundleKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
希望对您有所帮助。如果需要进一步的帮助,请告诉我。
您需要创建自己的 UICollectionViewLayout
,正如我在评论中所说,我们将从倒着开始,首先您需要在 ViewController
viewDidLoad
方法中添加此行
let semanticLayout = SemanticLayout()
semanticLayout.delegate = self
self.collectionView.collectionViewLayout = semanticLayout
这是您需要实现的委托
extension ViewController: SemanticLayoutDelegate{
func semanticDisposition() -> SemanticDisposition {
return SemanticDisposition.rigthToLeft
}
}
使用您的 ViewController 姓名代替 ViewController ofc
这里有 SemanticLayout
class,检查是否 完全可定制 您可以定义 UICollectionView
是 RTL 还是具有委托方法的 LTR semanticDisposition
import UIKit
protocol SemanticLayoutDelegate: UICollectionViewDelegateFlowLayout {
func semanticDisposition() -> SemanticDisposition
}
enum SemanticDisposition {
case leftToRigth
case rigthToLeft
}
class SemanticLayout: UICollectionViewLayout {
weak var delegate: SemanticLayoutDelegate!
fileprivate var cellPadding: CGFloat = 10
fileprivate var cache = [UICollectionViewLayoutAttributes]()
fileprivate var contentHeight: CGFloat = 0
private var rowsWidth : [CGFloat] = [0]
private var avaiableSpaces : [(Int,CGFloat)] = []
private var currentRow : Int = 0
private var rowHeigths : CGFloat = -1.0
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
private func availableWidthForRow(index:Int) -> CGFloat {
let ocuppedWidth = self.rowsWidth[index]
return self.contentWidth - ocuppedWidth
}
private func canAddCellAtRow(rowIndex:Int,size:CGSize) ->Bool
{
if(availableWidthForRow(index: rowIndex) >= size.width) {
return true
}
return false
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
// Check if cache is empty
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
let viewSize: CGSize = delegate.collectionView!(collectionView, layout: self, sizeForItemAt: indexPath)
if(self.rowHeigths < 0) {
self.rowHeigths = viewSize.height
}
let width = viewSize.width
let height = viewSize.height
var xOffset = self.rowsWidth[self.currentRow]
if(self.canAddCellAtRow(rowIndex: self.currentRow, size: viewSize)) {
if(self.delegate.semanticDisposition() == .rigthToLeft) {
xOffset = (contentWidth - self.rowsWidth[self.currentRow]) - width
}
} else {
self.currentRow += 1
self.rowsWidth.append(0.0)
xOffset = self.rowsWidth[self.currentRow]
if(self.delegate.semanticDisposition() == .rigthToLeft) {
xOffset = (contentWidth - self.rowsWidth[self.currentRow]) - width
}
}
let yOffset = CGFloat(self.currentRow) * self.rowHeigths
let frame = CGRect(x: xOffset, y: yOffset, width: width, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache.append(attributes)
contentHeight = max(contentHeight, frame.maxY)
self.rowsWidth[self.currentRow] += width
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
for attributes in cache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cache[indexPath.item]
}
}
结果 RTL
class RTLSupportedCollectionViewFlowLayout: UICollectionViewFlowLayout {
override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}
}
您可以使用这种流式布局。仅当所有单元格返回的项目大小不同时才需要它。