UICollectionView doesn't fill data from right to left in RTL

怎甘沉沦 提交于 2020-12-12 04:24:18

问题


I have a UICollectionView and want to display cells horizontally, e.g.

In English, it should display: | cellA | cellB | cellC |

In Arabic, it should display: [ cellC | cellB | cellA |

For RTL, the UICollectionViewFlowLayout works fine by default for me if every cell has the same size. However, if I implement collectionView:layout:sizeForItemAtIndexPath: and set different widths for each cell, the CollectionView becomes:

| cellA | cellB | cellC |

Is there any way to fix this?


回答1:


You need to create your own UICollectionViewLayout, as I said in my comment, we will start backwards, first you need to add this lines on your ViewController viewDidLoad method

let semanticLayout = SemanticLayout()
semanticLayout.delegate = self
self.collectionView.collectionViewLayout = semanticLayout

this is the delegate that you need to implement

extension ViewController: SemanticLayoutDelegate{
    func semanticDisposition() -> SemanticDisposition {
        return SemanticDisposition.rigthToLeft
    }   
}

using your ViewController name instead of ViewController ofc

And here you have the SemanticLayout class, check that is fully customizable you can define if your UICollectionView will be RTL or LTR with delegate method 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]
    }
}

Result RTL

LTR




回答2:


class RTLSupportedCollectionViewFlowLayout: UICollectionViewFlowLayout {

    override var flipsHorizontallyInOppositeLayoutDirection: Bool {
        return true
    }
}

you can use this flow layout. it is only needed when the item size returned is not same for all the cells.




回答3:


This class is working for me to display collection view from left to right with different sizes of cells.

#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

Hope this help. let me know if further help needed.



来源:https://stackoverflow.com/questions/54074028/uicollectionview-doesnt-fill-data-from-right-to-left-in-rtl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!