问题描述
我正在开发一个基本的密码输入屏幕,包括一个显示当前输入的顶部堆栈,然后是一些显示数字的 HStack
vstack(){
HStack(spacing: 20){
ForEach(codes,id: \.self){i in
Text("*")
}
}
HStack(){
<Number 1 - 3>
}
HStack(){
<Number 4 - 6>
}
HStack(){
<Number 7 - 9>
}
HStack(){
<Number 0>
}
}
我面临的这个问题是,当没有输入密码时,顶部 HStack 不会占用任何空间,因此垂直高度为 0,当我输入代码时,它会强制整个视图随着视图调整大小而跳跃一点.
我怎么能阻止它
解决方法
老实说,建造起来很有趣! ? 如果它解决了您的问题,请不要忘记将这个答案标记为正确的答案。 ✅
问题
跳跃效果是由于 SwiftUI 会根据根据您的内容(密码数字)计算出的可用空间更新所有视图位置。字体、字体粗细、文字大小等……都会影响其他视图的可用空间。
解决方案
为了避免这种情况,您需要一个预定义的框架,让父视图知道您的数字永远不会占用更多空间。这样做,每次更新都不会影响任何其他视图的位置,因为分配的顶部空间始终是您指定的大小,而不是数字大小(或不存在)。
代码
import SwiftUI
import Combine
// Using Combine to manage digits and future network calls…
class PasscodeManager: ObservableObject {
let codesQuantity = 4
@Published var codes = [Int]()
}
struct PasscodeView: View {
@StateObject private var manager = PasscodeManager()
var body: some View {
VStack {
Spacer()
// Dots placeholders and passcode digits
selectedCodes
Spacer()
// Numberpad
PasscodeLine(numbers: 1...3) { add(number: $0) }
PasscodeLine(numbers: 4...6) { add(number: $0) }
PasscodeLine(numbers: 7...9) { add(number: $0) }
PasscodeLine(numbers: 0...0) { add(number: $0) }
Spacer()
}
.padding()
}
var selectedCodes: some View {
let minDots = manager.codes.count == manager.codesQuantity ? 0:1
let maxDots = manager.codesQuantity - manager.codes.count
return HStack(spacing: 32) {
ForEach(manager.codes,id: \.self) { Text("\($0)") }
if maxDots != 0 {
ForEach(minDots...maxDots,id: \.self) { _ in
Circle().frame(width: 12)
}
}
}
.font(.title.bold())
// Setting a default height should fix your problem. ?
.frame(height: 70)
}
func add(number: Int) {
guard manager.codes.count < manager.codesQuantity else { return }
manager.codes.append(number)
}
}
struct PasscodeLine: View {
let numbers: ClosedRange<Int>
var select: (Int) -> Void
var body: some View {
HStack {
ForEach(numbers,id: \.self) { number in
Spacer()
Button(action: { select(number) },label: {
Text("\(number)")
.font(.title)
.fontWeight(.medium)
.foregroundColor(Color(.label))
.padding(32)
.background(Color(.quaternarySystemFill))
.clipShape(Circle())
})
}
Spacer()
}
}
}