-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathLZTagLayout.swift
185 lines (160 loc) · 8.71 KB
/
LZTagLayout.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//
// LZTagLayout.swift
// Tag
//
// Created by lizhi on 2022/3/1.
//
import UIKit
public protocol LZTagLayoutDelegate: NSObject {
/// 文字内容for cell
func tagLayout(_ layout: LZTagLayout, collectionView: UICollectionView, textForItemAt indexPath: IndexPath) -> String
/// section head footer size
func tagLayout(_ layout: LZTagLayout, collectionView: UICollectionView, sizeForSupplementaryElementOfKind kind: String, at section: Int) -> CGSize
/// 标签的内边距
func tagLayout(_ layout: LZTagLayout, collectionView: UICollectionView, tagInnerMarginForItemAt indexPath: IndexPath) -> CGFloat
}
// NSTextAlignment textAlignment
public enum TagContentAlignment {
case left
case right
case center
}
open class LZTagLayout: UICollectionViewLayout {
// 标签的内边距
// open var tagInnerMargin: CGFloat = 25
// 元素间距
open var itemSpacing: CGFloat = 10
// 行间距
open var lineSpacing: CGFloat = 10
// 标签的高度
open var itemHeight: CGFloat = 25
// 标签的字体
open var itemFont = UIFont.systemFont(ofSize: 12)
open weak var delegate: LZTagLayoutDelegate?
// var titles = [String]()
/// tag 内容的对齐方式
open var contentAlignment = TagContentAlignment.left
// 可见区域
private(set) var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
private var headerFooterLayoutAttributes = [UICollectionViewLayoutAttributes]()
// 内容高度
private(set) var contentHeight: CGFloat = 0
override open func prepare() {
guard let collectionView = self.collectionView, let delegate = self.delegate else { return }
let sections = collectionView.numberOfSections
contentHeight = 0
visibleLayoutAttributes.removeAll()
headerFooterLayoutAttributes.removeAll()
for section in 0 ..< sections {
let sectionIndexPath = IndexPath(item: 0, section: section)
let headerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: sectionIndexPath)
//head
let sectionHeadSize = delegate.tagLayout(self, collectionView: collectionView, sizeForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: section)
let sectionOriginY = contentHeight
let sectionHeaderFrame = CGRect(x: 0 , y: sectionOriginY , width: sectionHeadSize.width , height: sectionHeadSize.height)
headerAttribute.frame = sectionHeaderFrame
headerFooterLayoutAttributes.append(headerAttribute)
contentHeight += sectionHeadSize.height
// 处理tag
let rows = collectionView.numberOfItems(inSection: section)
var frame = CGRect(x: 0, y: contentHeight + lineSpacing, width: 0, height: 0)
var contentWidthInRow = CGFloat(0)
var indexPathsInRow = [IndexPath]()
for item in 0 ..< rows {
let indexPath = IndexPath(item: item, section: section)
let text = delegate.tagLayout(self, collectionView: collectionView, textForItemAt: indexPath)
let tagInnerMargin = delegate.tagLayout(self, collectionView: collectionView, tagInnerMarginForItemAt: indexPath)
let tagWidth = textWidth(text) + tagInnerMargin
switch contentAlignment {
case .left:
break
case .right:
if frame.maxX + tagWidth + itemSpacing * 2 > self.collectionView!.frame.width {
// 需要换行
resetRightAlignmentRowFrame(contentWidthInRow: contentWidthInRow, indexPathsInRow: indexPathsInRow)
}
case .center:
if frame.maxX + tagWidth + itemSpacing * 2 > self.collectionView!.frame.width {
// 需要换行
resetCenterAlignmentRowFrame(contentWidthInRow: contentWidthInRow, indexPathsInRow: indexPathsInRow)
}
}
// 正常靠左显示
if frame.maxX + tagWidth + itemSpacing * 2 > self.collectionView!.frame.width {
indexPathsInRow.removeAll()
contentWidthInRow = 0
// 需要换行
frame = CGRect(x: itemSpacing, y: frame.maxY + lineSpacing, width: tagWidth, height: itemHeight)
contentWidthInRow = itemSpacing + tagWidth
} else {
frame = CGRect(x: frame.maxX + itemSpacing, y: frame.origin.y, width: tagWidth, height: itemHeight)
contentWidthInRow = contentWidthInRow + itemSpacing + tagWidth
}
indexPathsInRow.append(indexPath)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = frame
visibleLayoutAttributes.removeAll { $0.indexPath == indexPath }
visibleLayoutAttributes.append(attributes)
}
if indexPathsInRow.isEmpty == false { // 最后一行重设frame
switch contentAlignment {
case .left:
break
case .right:
resetRightAlignmentRowFrame(contentWidthInRow: contentWidthInRow, indexPathsInRow: indexPathsInRow)
case .center:
resetCenterAlignmentRowFrame(contentWidthInRow: contentWidthInRow, indexPathsInRow: indexPathsInRow)
}
contentWidthInRow = 0
indexPathsInRow.removeAll()
}
contentHeight = frame.maxY + lineSpacing
//footer
let sectionFooterSize = delegate.tagLayout(self, collectionView: collectionView, sizeForSupplementaryElementOfKind: UICollectionView.elementKindSectionFooter, at: section)
let sectionFooterOriginY = contentHeight
let sectionFooterFrame = CGRect(x: 0 , y: sectionFooterOriginY , width: sectionFooterSize.width , height: sectionFooterSize.height)
let footerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: sectionIndexPath)
footerAttribute.frame = sectionFooterFrame
headerFooterLayoutAttributes.append(footerAttribute)
contentHeight = contentHeight + sectionFooterSize.height
}
func resetCenterAlignmentRowFrame(contentWidthInRow: CGFloat, indexPathsInRow: [IndexPath]) {
let offset = ((self.collectionView?.frame.size.width ?? 0) - contentWidthInRow - itemSpacing) / 2
for indexPath in indexPathsInRow {
let attribute = visibleLayoutAttributes.first { $0.indexPath == indexPath }
if let centerOld = attribute?.center {
attribute?.center = CGPoint(x: centerOld.x + offset, y: centerOld.y)
}
}
}
func resetRightAlignmentRowFrame(contentWidthInRow: CGFloat, indexPathsInRow: [IndexPath]) {
let offset = ((self.collectionView?.frame.size.width ?? 0) - contentWidthInRow - itemSpacing)
for indexPath in indexPathsInRow {
let attribute = visibleLayoutAttributes.first { $0.indexPath == indexPath }
if let centerOld = attribute?.center {
attribute?.center = CGPoint(x: centerOld.x + offset, y: centerOld.y)
}
}
}
}
override open var collectionViewContentSize: CGSize {
return CGSize(width: collectionView?.frame.size.width ?? 0, height: contentHeight)
}
override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return visibleLayoutAttributes + headerFooterLayoutAttributes
// return visibleLayoutAttributes
}
override open func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAttribute = visibleLayoutAttributes.first { $0.indexPath == indexPath }
return layoutAttribute
}
open override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAttribute = headerFooterLayoutAttributes.first {( $0.indexPath == indexPath) && ($0.representedElementKind == elementKind) }
return layoutAttribute
}
// 根据文字 确定label的宽度
func textWidth(_ text: String) -> CGFloat {
let rect = (text as NSString).boundingRect(with: .zero, options: .usesLineFragmentOrigin, attributes: [.font: itemFont], context: nil)
return rect.width
}
}