我有一個帶間隔和文本的 HStack。
HStack {
Spacer()
Text("Some Text")
Spacer()
}
現在我希望第一個 Spacer 占用 X% 的可用空間(不包括 占用的空間Text),而底部占用其余空間。
如果我使用 Geometry Reader,并對第一個 Spacer 執行以下操作 -
Spacer()
.frame(width: geometry.size.width * (X/100))
它沒有考慮Text.
有沒有辦法在墊片之間劃分“可用”空間?
uj5u.com熱心網友回復:
您正在尋找的是PreferenceKeys. 本質上,它們會跟蹤某些視圖的大小,然后您可以使用這些大小來計算您需要的內容。它們最常用于使多個視圖保持相同大小,即使它們具有不同的大小。我給你一個長短的解決方案:
長解決方案:
struct SpacerPrefKeyView: View {
@State private var textWidth: CGFloat = 10
@State private var hStackWidth: CGFloat = 10
let X: CGFloat = 20
var body: some View {
HStack {
Spacer()
.frame(width: (hStackWidth - textWidth) * (X/100))
Text("Hello, World!")
.background(GeometryReader { geometry in
Color.clear.preference(
//This sets the preference key value with the width of the background view
key: TextWidthPrefKey.self,
value: geometry.size.width)
})
Spacer()
}
.background(GeometryReader { geometry in
Color.clear.preference(
//This sets the preference key value with the width of the background view
key: HStackWidthPrefKey.self,
value: geometry.size.width)
})
.onPreferenceChange(TextWidthPrefKey.self) {
// This keeps track of the change of the size
textWidth = $0
}
.onPreferenceChange(HStackWidthPrefKey.self) {
// This keeps track of the change of the size
hStackWidth = $0
}
}
}
private extension SpacerPrefKeyView {
struct TextWidthPrefKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat,
nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}
struct HStackWidthPrefKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat,
nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}
}
感謝 FiveStarBlog 和 Twitter 上的@ramzesenok 的簡短解決方案:
struct SpacerPrefKeyView: View {
@State private var textSize: CGSize = CGSize(width: 10, height: 10)
@State private var hStackSize: CGSize = CGSize(width: 10, height: 10)
let X: CGFloat = 20
var body: some View {
HStack {
Spacer()
.frame(width: (hStackSize.width - textSize.width) * (X/100))
Text("Hello, World!")
.copySize(to: $textSize)
Spacer()
}
.copySize(to: $hStackSize)
}
}
并使用擴展名:
extension View {
func readSize(onChange: @escaping (CGSize) -> Void) -> some View {
background(
GeometryReader { geometryProxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: geometryProxy.size)
}
)
.onPreferenceChange(SizePreferenceKey.self, perform: onChange)
}
func copySize(to binding: Binding<CGSize>) -> some View {
self.readSize { size in
binding.wrappedValue = size
}
}
}
他們做同樣的事情,但是擴展非常巧妙地處理它,而您的視圖中沒有額外的代碼。我把它貼出來讓你看看它是如何PreferenceKeys作業的。
uj5u.com熱心網友回復:
這是一個體面的解決方案,只需要一個GeometryReader.
有關更多資訊,請查看這篇文章。
struct MyView: View {
@State private var offset: CGFloat?
var body: some View {
HStack {
Spacer()
Text("Some Text")
.anchorPreference(key: BoundsPreference.self, value: .bounds) { $0 }
Spacer(minLength: offset)
}
.backgroundPreferenceValue(BoundsPreference.self) { preferences in
GeometryReader { g in
preferences.map {
Color.clear.preference(key: OffsetPreference.self, value: offset(with: g.size.width - g[$0].width))
}
}
}
.onPreferenceChange(OffsetPreference.self) {
offset = $0
}
}
private func offset(with widthRemainder: CGFloat) -> CGFloat {
widthRemainder * (100.0 - X) / 100.0
}
}
private struct BoundsPreference: PreferenceKey {
typealias Value = Anchor<CGRect>?
static var defaultValue: Value = nil
static func reduce(value: inout Anchor<CGRect>?, nextValue: () -> Anchor<CGRect>?) {
value = nextValue()
}
}
private struct OffsetPreference: PreferenceKey {
typealias Value = CGFloat?
static var defaultValue: Value = nil
static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
value = nextValue()
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/413439.html
標籤:
