UICollection View Flow Layout Vertical Align

Па змаўчанні, калі вы выкарыстоўваеце макет патоку ў мэтах збору, клетка вертыкальна-цэнтры. Ці ёсць спосаб, каб змяніць гэта выраўноўванне?

enter image description here

18
Ніжняя выраўноўванне (уздоўж чырвонай лініі). Але знізу або зверху, гэта не важна, спосаб зрабіць гэта павінна быць тое ж самае, праўда?
дададзена аўтар R.Lambert, крыніца
тое, што выраўноўванне/дызайн вы хочаце ??
дададзена аўтар Anil Varghese, крыніца

9 адказы

Наступны код працуе для мяне

@interface TopAlignedCollectionViewFlowLayout : UICollectionViewFlowLayout

- (void)alignToTopForSameLineElements:(NSArray *)sameLineElements;

@end

@implementation TopAlignedCollectionViewFlowLayout

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
{
    NSArray *attrs = [super layoutAttributesForElementsInRect:rect];
    CGFloat baseline = -2;
    NSMutableArray *sameLineElements = [NSMutableArray array];
    for (UICollectionViewLayoutAttributes *element in attrs) {
        if (element.representedElementCategory == UICollectionElementCategoryCell) {
            CGRect frame = element.frame;
            CGFloat centerY = CGRectGetMidY(frame);
            if (ABS(centerY - baseline) > 1) {
                baseline = centerY;
                [self alignToTopForSameLineElements:sameLineElements];
                [sameLineElements removeAllObjects];
            }
            [sameLineElements addObject:element];
        }
    }
    [self alignToTopForSameLineElements:sameLineElements];//align one more time for the last line
    return attrs;
}

- (void)alignToTopForSameLineElements:(NSArray *)sameLineElements
{
    if (sameLineElements.count == 0) {
        return;
    }
    NSArray *sorted = [sameLineElements sortedArrayUsingComparator:^NSComparisonResult(UICollectionViewLayoutAttributes *obj1, UICollectionViewLayoutAttributes *obj2) {
        CGFloat height1 = obj1.frame.size.height;
        CGFloat height2 = obj2.frame.size.height;
        CGFloat delta = height1 - height2;
        return delta == 0. ? NSOrderedSame : ABS(delta)/delta;
    }];
    UICollectionViewLayoutAttributes *tallest = [sorted lastObject];
    [sameLineElements enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger idx, BOOL *stop) {
        obj.frame = CGRectOffset(obj.frame, 0, tallest.frame.origin.y - obj.frame.origin.y);
    }];
}

@end
24
дададзена
Па нейкай прычыне гэта будзе працаваць часам, але тады я б прагартаць і клеткі папярэдне выраўнаваныя зверху затым па цэнтры. Вельмі дзіўна.
дададзена аўтар rob5408, крыніца
ды, здаецца, працуе, але крыху дзіўна. Гэта не падобна на працу на прашыўцы 9. Любога прапанову, дапамогу. дзякуй
дададзена аўтар Frade, крыніца
Выпрабавана кучу рашэнняў, гэта было толькі адзін працуе, як чакалася.
дададзена аўтар Johannes, крыніца

@DongXu: Ваша рашэнне працаваў для мяне таксама. Вось версія Свіфт, калі:

class TopAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout
{
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]?
    {
        if let attrs = super.layoutAttributesForElementsInRect(rect)
        {
            var baseline: CGFloat = -2
            var sameLineElements = [UICollectionViewLayoutAttributes]()
            for element in attrs
            {
                if element.representedElementCategory == .Cell
                {
                    let frame = element.frame
                    let centerY = CGRectGetMidY(frame)
                    if abs(centerY - baseline) > 1
                    {
                        baseline = centerY
                        TopAlignedCollectionViewFlowLayout.alignToTopForSameLineElements(sameLineElements)
                        sameLineElements.removeAll()
                    }
                    sameLineElements.append(element)
                }
            }
            TopAlignedCollectionViewFlowLayout.alignToTopForSameLineElements(sameLineElements)//align one more time for the last line
            return attrs
        }
        return nil
    }

    private class func alignToTopForSameLineElements(sameLineElements: [UICollectionViewLayoutAttributes])
    {
        if sameLineElements.count < 1
        {
            return
        }
        let sorted = sameLineElements.sort { (obj1: UICollectionViewLayoutAttributes, obj2: UICollectionViewLayoutAttributes) -> Bool in

            let height1 = obj1.frame.size.height
            let height2 = obj2.frame.size.height
            let delta = height1 - height2
            return delta <= 0
        }
        if let tallest = sorted.last
        {
            for obj in sameLineElements
            {
                obj.frame = CGRectOffset(obj.frame, 0, tallest.frame.origin.y - obj.frame.origin.y)
            }
        }
    }
}
17
дададзена
Вы можаце пазбавіцца ад , калі пункт ўнутры для цыклу, як гэта: для элемента ў ATTRS дзе element.representedElementCategory == .cell {. , , }
дададзена аўтар kikettas, крыніца

Гэта можа ці не можа працаваць для вашай канкрэтнай сітуацыі, але ў мяне было крыху ўдачы падкласаў UICollectionViewFlowLayout наступным чынам:

@interface CustomFlowLayout : UICollectionViewFlowLayout
@end

@implementation CustomFlowLayout

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSArray* attributesToReturn = [super layoutAttributesForElementsInRect:rect];
    for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
        if (nil == attributes.representedElementKind) {
            NSIndexPath* indexPath = attributes.indexPath;
            attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
        }
    }
    return attributesToReturn;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *currentItemAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];

    currentItemAttributes.frame = CGRectOffset(currentItemAttributes.frame, 0, 0.5 * CGRectGetHeight(currentItemAttributes.frame));

    return currentItemAttributes;
}

@end
3
дададзена

Swift 3 Version in case someone just wants to Copy & Paste:

class TopAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        if let attrs = super.layoutAttributesForElements(in: rect) {
            var baseline: CGFloat = -2
            var sameLineElements = [UICollectionViewLayoutAttributes]()
            for element in attrs {
                if element.representedElementCategory == .cell {
                    let frame = element.frame
                    let centerY = frame.midY
                    if abs(centerY - baseline) > 1 {
                        baseline = centerY
                        alignToTopForSameLineElements(sameLineElements: sameLineElements)
                        sameLineElements.removeAll()
                    }
                    sameLineElements.append(element)
                }
            }
            alignToTopForSameLineElements(sameLineElements: sameLineElements)//align one more time for the last line
            return attrs
        }
        return nil
    }

    private func alignToTopForSameLineElements(sameLineElements: [UICollectionViewLayoutAttributes]) {
        if sameLineElements.count < 1 { return }
        let sorted = sameLineElements.sorted { (obj1: UICollectionViewLayoutAttributes, obj2: UICollectionViewLayoutAttributes) -> Bool in
            let height1 = obj1.frame.size.height
            let height2 = obj2.frame.size.height
            let delta = height1 - height2
            return delta <= 0
        }
        if let tallest = sorted.last {
            for obj in sameLineElements {
                obj.frame = obj.frame.offsetBy(dx: 0, dy: tallest.frame.origin.y - obj.frame.origin.y)
            }
        }
    }
}
2
дададзена

Я выкарыстаў нешта падобнае перш, чым адказы. У маім выпадку я хачу, каб выраўнаваць клеткі, Colum з рознай вышынёй.

import UIKit

class AlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        if let attributes = super.layoutAttributesForElements(in: rect) {
            let sectionElements: [Int : [UICollectionViewLayoutAttributes]] = attributes
                .filter {
                    return $0.representedElementCategory == .cell //take cells only
                }.groupBy {
                    return $0.indexPath.section //group attributes by section
            }

            sectionElements.forEach { (section, elements) in
                //get suplementary view (header) to align each section
                let suplementaryView = attributes.first {
                    return $0.representedElementCategory == .supplementaryView && $0.indexPath.section == section
                }
                //call align method
                alignToTopSameSectionElements(elements, with: suplementaryView)
            }

            return attributes
        }

        return super.layoutAttributesForElements(in: rect)
    }

    private func alignToTopSameSectionElements(_ elements: [UICollectionViewLayoutAttributes], with suplementaryView: UICollectionViewLayoutAttributes?) {
        //group attributes by colum 
        let columElements: [Int : [UICollectionViewLayoutAttributes]] = elements.groupBy {
            return Int($0.frame.midX)
        }

        columElements.enumerated().forEach { (columIndex, object) in
            let columElement = object.value.sorted {
                return $0.indexPath < $1.indexPath
            }

            columElement.enumerated().forEach { (index, element) in
                var frame = element.frame

                if columIndex == 0 {
                    frame.origin.x = minimumLineSpacing
                }

                switch index {
                case 0:
                    if let suplementaryView = suplementaryView {
                        frame.origin.y = suplementaryView.frame.maxY
                    }
                default:
                    let beforeElement = columElement[index-1]
                    frame.origin.y = beforeElement.frame.maxY + minimumInteritemSpacing
                }

                element.frame = frame
            }
        }
    }
}

public extension Array {

    func groupBy  (groupingFunction group: (Element) -> U) -> [U: Array] {

        var result = [U: Array]()

        for item in self {

            let groupKey = group(item)

            if result.has(groupKey) {
                result[groupKey]! += [item]
            } else {
                result[groupKey] = [item]
            }
        }

        return result
    }
}

<�Моцны> Гэта вынік гэтай схемы:

enter image description here

2
дададзена
Прывітанне @ GOrozco58, я імкнуся свой код. Вертыкальнае прастору паміж элементамі не зьяўляюцца ідэнтычнымі, некаторыя дэталі маюць больш прасторы, некаторыя мае менш месцы. Вы ведаеце, як вырашыць гэтую праблему?
дададзена аўтар H S Warraich, крыніца

Свіфт 4 з функцыянальным арыентаваным падыходам:

class TopAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)?
            .map { $0.copy() } as? [UICollectionViewLayoutAttributes]

        attributes?
            .filter { $0.representedElementCategory == .cell }
            .reduce([:]) {
                $0.merging([ceil($1.center.y): [$1]]) {
                    $0 + $1
                }
            }
            .values.forEach { line in
                let maxHeightY = line.max {
                    $0.frame.size.height < $1.frame.size.height
                }?.frame.origin.y

                line.forEach {
                    $0.frame = $0.frame.offsetBy(
                        dx: 0,
                        dy: (maxHeightY ?? $0.frame.origin.y) - $0.frame.origin.y
                    )
                }
            }

        return attributes
    }
}
1
дададзена

Я выкарыстаў гэты код ( https://github.com/yoeriboven/TopAlignedCollectionViewLayout ) пасля таго, як рашэнне Dongxu не зрабiў дастаткова працы. Адзінае змяненне ў тым, што яна першапачаткова прызначана для выкарыстання з сеткай, так што мне трэба, каб стварыць асобнік макета з адвольна вялікай колькасцю адлікаў калоны ...

let collectionViewFlowLayout = YBTopAlignedCollectionViewFlowLayout(numColumns: 1000)
0
дададзена

<�Код> UICollectionViewFlowLayout клас з'яўляецца вытворным ад UICollectionViewLayout базавага класа. І калі вы паглядзіце на дакументацыя для гэтага , вы ўбачыце, існуе цэлы шэраг метадаў, якія вы можаце перавызначыць, найбольш верагодным кандыдатам быўшы layoutAttributesForItemAtIndexPath: .

Калі перавызначыць гэты метад, вы можаце дазволіць ёй выклікаць яго супер рэалізацыю, а затым наладзіць ўласцівасці вернутага UICollectionViewLayoutAttributes аб'екта. У прыватнасці, вы, верагодна, трэба наладзіць кадр ўласцівасць не перамясціць элемент, так што гэта ўжо не па цэнтры.

0
дададзена
Дзякуй, я паспрабую з гэтым рашэннем.
дададзена аўтар R.Lambert, крыніца

@ адказаць правільна. Тым не менш, я прапаную, каб зрабіць гэтыя разлікі ў UICollectionViewFlowLayout 's падрыхтаваць() метад. Гэта дазволіць прадухіліць множныя разлікі на тыя ж атрыбуты клеткі. Акрамя таго, падрыхтаваць() гэта лепшае месца для кіравання кэшам атрыбутаў.

0
дададзена