我使用GeometryReader和ZStack以及.position修饰符成功实现了某些功能。为获取字符串宽度,我使用了一种黑客方法使用UIFont,但是由于你正在处理图像,因此宽度应该更容易获得。
下面的视图具有垂直和水平对齐的状态变量,让您可以从ZStack的任何角落开始。可能会增加不必要的复杂性,但您应该能够根据自己的需求进行调整。
import SwiftUI
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
return ceil(boundingBox.height)
}
func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
return ceil(boundingBox.width)
}
}
struct WrapStack: View {
var strings: [String]
@State var borderColor = Color.red
@State var verticalAlignment = VerticalAlignment.top
@State var horizontalAlignment = HorizontalAlignment.leading
var body: some View {
GeometryReader { geometry in
ZStack {
ForEach(self.strings.indices, id: \.self) {idx in
Text(self.strings[idx])
.position(self.nextPosition(
index: idx,
bucketRect: geometry.frame(in: .local)))
}
}
}
.overlay(Rectangle().stroke(self.borderColor))
}
func nextPosition(index: Int,
bucketRect: CGRect) -> CGPoint {
let ssfont = UIFont.systemFont(ofSize: UIFont.systemFontSize)
let initX = (self.horizontalAlignment == .trailing) ? bucketRect.size.width : CGFloat(0)
let initY = (self.verticalAlignment == .bottom) ? bucketRect.size.height : CGFloat(0)
let dirX = (self.horizontalAlignment == .trailing) ? CGFloat(-1) : CGFloat(1)
let dirY = (self.verticalAlignment == .bottom) ? CGFloat(-1) : CGFloat(1)
let internalPad = 10
var runningX = initX
var runningY = initY
let fontHeight = "TEST".height(withConstrainedWidth: 30, font: ssfont)
if index > 0 {
for i in 0...index-1 {
let w = self.strings[i].width(
withConstrainedHeight: fontHeight,
font: ssfont) + CGFloat(internalPad)
if dirX <= 0 {
if (runningX - w) <= 0 {
runningX = initX - w
runningY = runningY + dirY * fontHeight
} else {
runningX -= w
}
} else {
if (runningX + w) >= bucketRect.size.width {
runningX = initX + w
runningY = runningY + dirY * fontHeight
} else {
runningX += w
}
}
}
}
let w = self.strings[index].width(
withConstrainedHeight: fontHeight,
font: ssfont) + CGFloat(internalPad)
if dirX <= 0 {
if (runningX - w) <= 0 {
runningX = initX
runningY = runningY + dirY * fontHeight
}
} else {
if (runningX + w) >= bucketRect.size.width {
runningX = initX
runningY = runningY + dirY * fontHeight
}
}
return CGPoint(
x: runningX + dirX * w/2,
y: runningY + dirY * fontHeight/2)
}
}
struct WrapStack_Previews: PreviewProvider {
static var previews: some View {
WrapStack(strings: ["One, ", "Two, ", "Three, ", "Four, ", "Five, ", "Six, ", "Seven, ", "Eight, ", "Nine, ", "Ten, ", "Eleven, ", "Twelve, ", "Thirteen, ", "Fourteen, ", "Fifteen, ", "Sixteen"])
}
}