Last active
June 3, 2024 04:39
-
-
Save Koshimizu-Takehito/03c1302dbe0283c5b01eaee770afdba1 to your computer and use it in GitHub Desktop.
改行を考慮した FlowLayout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct FlowLayoutSampleView: View { | |
@State var width: CGFloat = 180 | |
let tags: [String] = [ | |
"Objective-C", | |
"Swift", | |
// "SwiftSwiftSwiftSwiftSwiftSwiftSwiftSwift", | |
// "SwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwiftSwift", | |
"Ruby", "Python", "JavaScript", | |
"Java", "C++", "C#", "Go", "Kotlin", "Rust" | |
] | |
var body: some View { | |
VStack { | |
Spacer() | |
Slider(value: $width.animation(), in: 100...360) | |
} | |
.background { | |
MyFlowLayout(vSpacing: 8.0, hSpacing: 8.0) { | |
ForEach(tags, id: \.self) { tag in | |
Text(tag) | |
.lineLimit(nil) | |
.font(.body) | |
.fontWeight(.semibold) | |
.fontDesign(.monospaced) | |
.foregroundStyle(.white) | |
.padding(.vertical, 6) | |
.padding(.horizontal, 12) | |
.background(.black) | |
.clipShape(RoundedRectangle(cornerRadius: 16)) | |
} | |
} | |
.frame(maxWidth: width) | |
.background(.pink.opacity(0.2)) | |
} | |
.padding(20) | |
} | |
} | |
struct MyFlowLayout: Layout { | |
var vSpacing: CGFloat = 8.0 | |
var hSpacing: CGFloat = 8.0 | |
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { | |
let proposalWidth = proposal.width ?? .zero | |
var remainWidth = proposalWidth - hSpacing | |
var currentHeight = CGFloat.zero | |
var totalSize = CGSize.zero | |
for subview in subviews { | |
let size = subview.sizeThatFits(.init(width: proposalWidth - 2 * hSpacing, height: .infinity)) | |
if remainWidth - (size.width + hSpacing) < 0 { | |
totalSize.height += currentHeight | |
remainWidth = proposalWidth - hSpacing | |
currentHeight = .zero | |
} | |
remainWidth -= size.width + hSpacing | |
currentHeight = max(size.height + vSpacing, currentHeight) | |
} | |
totalSize.height += currentHeight + vSpacing | |
totalSize.width = proposalWidth | |
return totalSize | |
} | |
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { | |
var offset = CGPoint.zero | |
offset.y += vSpacing | |
offset.x += hSpacing | |
var remainWidth = bounds.width | |
var currentHeight = CGFloat.zero | |
for subview in subviews { | |
let size = subview.sizeThatFits(.init(width: bounds.width - 2 * hSpacing, height: .infinity)) | |
if remainWidth - (size.width + hSpacing) < 0 { | |
offset.y += currentHeight + vSpacing | |
offset.x = hSpacing | |
currentHeight = .zero | |
remainWidth = (bounds.width - hSpacing) | |
} | |
let point = CGPoint(x: bounds.origin.x + offset.x, y: bounds.origin.y + offset.y) | |
subview.place(at: point, proposal: .init(size)) | |
offset.x += size.width + hSpacing | |
remainWidth -= size.width + hSpacing | |
currentHeight = max(size.height, currentHeight) | |
} | |
} | |
} | |
#Preview { | |
FlowLayoutSampleView() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment