我从这个开始。一些带有两个滑块的UI。
由以下代码定义:
struct ContentView: View {
@State private var num1: Double = 0.5
@State private var num2: Double = 0.5
let blueGreen = Color(red: 0.2, green: 0.6, blue: 0.6)
var body: some View {
VStack {
Circle().fill(blueGreen).border(Color.blue, width: 1.0).padding(4.0)
VStack {
HStack {
Text("Value:")
Slider(value: $num1, in: 0...1)
}
HStack {
Text("Opacity:")
Slider(value: $num2, in: 0...1)
}
}
Spacer()
}.border(Color.green, width: 1.0).padding()
}
}
我希望“值:”和“不透明度:”标签在其后沿对齐,使滑块对齐并保持相同的宽度。我听说自定义对齐方式可以对齐这样的视图,即非同级视图。
因此,我添加了自定义对齐方式:
extension HorizontalAlignment {
private enum MyAlignment : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d[.trailing]
}
}
static let myAlignment = HorizontalAlignment(MyAlignment.self)
}
struct ContentView: View {
@State private var num1: Double = 0.5
@State private var num2: Double = 0.5
let blueGreen = Color(red: 0.2, green: 0.6, blue: 0.6)
var body: some View {
VStack {
Circle().fill(blueGreen).border(Color.blue, width: 1.0).padding(4.0)
VStack(alignment: .myAlignment) {
HStack {
Text("Value:").alignmentGuide(.myAlignment) { d in d[.trailing] }
Slider(value: $num1, in: 0...1)
}
HStack {
Text("Opacity:").alignmentGuide(.myAlignment) { d in d[.trailing] }
Slider(value: $num2, in: 0...1)
}
}
Spacer()
}.border(Color.green, width: 1.0).padding()
}
}
部分起作用。标签的后沿对齐,但是现在UI已部分向右移出屏幕。
使用UIKit和自动布局,我可以将这两个标签的后沿约束为相等。那样不会将任何东西推离屏幕(像往常一样,假设一切都局限于屏幕边缘)。这是怎么了?以及如何通过对齐或更好的方式通过SwiftUI实现此目标?
答案 0 :(得分:2)
一种方法是使用偏好设置(了解更多here)。
struct ContentView: View {
@State private var num1: Double = 0.5
@State private var num2: Double = 0.5
@State private var sliderWidth: CGFloat = 0
private let spacing: CGFloat = 5
let blueGreen = Color(red: 0.2, green: 0.6, blue: 0.6)
var body: some View {
VStack {
Circle().fill(blueGreen).border(Color.blue, width: 1.0).padding(4.0)
VStack(alignment: .trailing) {
HStack(spacing: self.spacing) {
Text("Value:")
.fixedSize()
.anchorPreference(key: MyPrefKey.self, value: .bounds, transform: { [$0] })
Slider(value: $num1, in: 0...1)
.frame(width: sliderWidth)
}
.frame(maxWidth: .infinity, alignment: .trailing)
HStack(spacing: self.spacing) {
Text("Opacity:")
.fixedSize()
.anchorPreference(key: MyPrefKey.self, value: .bounds, transform: { [$0] })
Slider(value: $num2, in: 0...1)
.frame(width: sliderWidth)
}
.frame(maxWidth: .infinity, alignment: .trailing)
}
.backgroundPreferenceValue(MyPrefKey.self) { prefs -> GeometryReader<AnyView> in
GeometryReader { proxy -> AnyView in
let vStackWidth = proxy.size.width
let maxAnchor = prefs.max {
return proxy[$0].size.width < proxy[$1].size.width
}
DispatchQueue.main.async {
if let a = maxAnchor {
self.sliderWidth = vStackWidth - (proxy[a].size.width + self.spacing)
}
}
return AnyView(EmptyView())
}
}
Spacer()
}.border(Color.green, width: 1.0).padding(20)
}
}
struct MyPrefKey: PreferenceKey {
typealias Value = [Anchor<CGRect>]
static var defaultValue: [Anchor<CGRect>] = []
static func reduce(value: inout [Anchor<CGRect>], nextValue: () -> [Anchor<CGRect>]) {
value.append(contentsOf: nextValue())
}
}
答案 1 :(得分:0)
嗯,我同意Apple似乎忘了在SwiftUI中使用自动布局引擎给我们一些东西,例如内容具有优先权,抗压缩性,大小相等等等...
在SwiftUI对齐指南中不会更改视图大小,它们只是按原样移动视图以对齐指定的锚点(如果大小不受限制,则扩展容器,因为默认情况下会紧缩内容,这就是您看到的内容) 。因此,要解决所考虑的情况,就需要以某种方式限制(或更改)大小。
下面的代码不是完整的解决方案(它没有考虑可能的10n),但是显示了方向-由于Slider
没有默认大小,因此应限制如何不扩展容器。
无论如何,我都会在下面将其视为解决方法...(宽度的计算可能更准确,更复杂,例如通过锚定首选项获取Text的帧,然后考虑可用填充从可用的几何代理宽度中减去,但这还是一样-明确指定视图框架的宽度)。
struct HueSaturationView : View {
@State private var num1: Double = 0.5
@State private var num2: Double = 0.5
let blueGreen = Color(red: 0.2, green: 0.6, blue: 0.6)
var body: some View {
GeometryReader { gp in
VStack {
Circle().fill(self.blueGreen).border(Color.blue, width: 1.0).padding(4.0)
VStack(alignment: .myAlignment) {
HStack {
Text("Value:").alignmentGuide(.myAlignment) { d in d[.trailing] }
Slider(value: self.$num1, in: 0...1)
.frame(width: gp.size.width * 2/3)
}
HStack {
Text("Opacity:").alignmentGuide(.myAlignment) { d in d[.trailing] }
Slider(value: self.$num2, in: 0...1)
.frame(width: gp.size.width * 2/3)
}
}
Spacer()
}
.border(Color.green, width: 1.0).padding()
}
}
}