Я использовал нечто похожее на предыдущие ответы. В моем случае я хочу выровнять клетки по столбам разной высоты.
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 [110].representedElementCategory == .cell //take cells only
}.groupBy {
return [110].indexPath.section //group attributes by section
}
sectionElements.forEach { (section, elements) in
//get suplementary view (header) to align each section
let suplementaryView = attributes.first {
return [110].representedElementCategory == .supplementaryView && [110].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([110].frame.midX)
}
columElements.enumerated().forEach { (columIndex, object) in
let columElement = object.value.sorted {
return [110].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
}
}
Это результат этого макета:
Хорошо, оказалось, что есть некоторые переопределения для SetParameter, которые позволяют явно устанавливать тип. Например:
query.SetParameter(position, null, NHibernateUtil.Int32);
Теперь методы полного расширения (только для Int32 и DateTime):
public static class QueryExtensions
{
public static void SetInt32(this IQuery query, int position, int? val)
{
if (val.HasValue)
{
query.SetInt32(position, val.Value);
}
else
{
query.SetParameter(position, null, NHibernateUtil.Int32);
}
}
public static void SetInt32(this IQuery query, string name, int? val)
{
if (val.HasValue)
{
query.SetInt32(name, val.Value);
}
else
{
query.SetParameter(name, null, NHibernateUtil.Int32);
}
}
public static void SetDateTime(this IQuery query, int position, DateTime? val)
{
if (val.HasValue)
{
query.SetDateTime(position, val.Value);
}
else
{
query.SetParameter(position, null, NHibernateUtil.DateTime);
}
}
public static void SetDateTime(this IQuery query, string name, DateTime? val)
{
if (val.HasValue)
{
query.SetDateTime(name, val.Value);
}
else
{
query.SetParameter(name, null, NHibernateUtil.DateTime);
}
}
}