Как изменить фиксированный размер элемента Lazy Grid GridItem в ответ на изменение размера динамического текста?
В приведенном ниже коде используется LazyVGrid
реализовать макет моих элементов управления так, чтобы все выстроилось следующим образом:
В частности, все концы ползунков выровнены, а символы выровнены по центру друг относительно друга. Я разработал, как подобрать размер GridItems для символа и числового считывания таким образом, чтобы они были "правильного размера" для своего содержимого - чего не могут сделать ни гибкие, ни адаптивные элементы GridItems - с учетом динамического типа.
При тестировании это работает очень хорошо, если пользователь не меняет размер своего динамического типа после того, как приложение активно. Если это будет сделано, тип (и символы) будут адаптироваться должным образом, но размер GridItem останется неизменным на своем начальном значении. Это приводит к переносу чисел в десятичную точку.
Есть ли способ изменить размер GridItem в ответ на изменения динамического типа или есть лучший способ сделать этот макет?
import SwiftUI
struct ProtoGrid: View {
let gridItems = [
GridItem(.fixed(UIImage(systemName: "ruler", withConfiguration: UIImage.SymbolConfiguration(textStyle: .body, scale: .large))!.size.width)),
GridItem(.flexible(minimum: 40, maximum: .infinity)),
GridItem(.fixed(("00.00" as NSString).size(withAttributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .body)]).width + 4), alignment: .trailing)
]
@State var index1 = 5.0
@State var index2 = 5.0
@State var index3 = 5.0
var body: some View {
VStack {
Rectangle()
.fill(Color.red)
LazyVGrid(columns: gridItems, spacing: 12) {
Image(systemName: "person").imageScale(.large)
Slider(value: $index1, in: 0...10)
Text("\(String(format: "%.2f", index1))").font(Font.system(.body).monospacedDigit())
Image(systemName: "megaphone").imageScale(.large)
Slider(value: $index2, in: 0...10)
Text("\(String(format: "%.2f", index2))").font(Font.system(.body).monospacedDigit())
Image(systemName: "ruler").imageScale(.large)
Slider(value: $index3, in: 0...10)
Text("\(String(format: "%.2f", index3))").font(Font.system(.body).monospacedDigit())
}
.padding()
}
}
}
struct ProtoGrid_Previews: PreviewProvider {
static var previews: some View {
ProtoGrid()
.previewDevice("iPhone 11 Pro")
}
}
111
0 ответов
Кажется очевидным, что цель вашей попытки использовать здесь сетку, чтобы ползунки были выровнены из-за разных размеров изображения, но конфигурация сетки постоянна, то есть вы сделали это один раз. На самом деле, он довольно жестко запрограммирован, поэтому не подходит для динамического текста.
Я бы предложил альтернативный подход - просто используйте обычный HStack, который динамически соответствует содержимому, и некоторое настраиваемое динамическое выравнивание содержимого.
Протестировано с Xcode 12 / iOS 14
struct ProtoGrid: View {
@State var index1 = 5.0
@State var index2 = 5.0
@State var index3 = 5.0
@State private var imageWidth = CGFloat.zero
var body: some View {
VStack {
Rectangle()
.fill(Color.red)
HStack(spacing: 12) {
Image(systemName: "person").imageScale(.large)
.alignedView(width: $imageWidth)
Slider(value: $index1, in: 0...10)
Text("\(String(format: "%.2f", index1))").font(Font.system(.body).monospacedDigit())
}
HStack(spacing: 12) {
Image(systemName: "megaphone").imageScale(.large)
.alignedView(width: $imageWidth)
Slider(value: $index2, in: 0...10)
Text("\(String(format: "%.2f", index2))").font(Font.system(.body).monospacedDigit())
}
HStack(spacing: 12) {
Image(systemName: "ruler").imageScale(.large)
.alignedView(width: $imageWidth)
Slider(value: $index3, in: 0...10)
Text("\(String(format: "%.2f", index3))").font(Font.system(.body).monospacedDigit())
}
}
.padding()
}
}
extension View {
func alignedView(width: Binding<CGFloat>) -> some View {
self.modifier(AlignedWidthView(width: width))
}
}
// creates a view which uses max width of calculated intrinsic
// content or shared from external width, updating external
// bound variable if own width is bigger.
struct AlignedWidthView: ViewModifier {
@Binding var width: CGFloat
func body(content: Content) -> some View {
content
.background(GeometryReader {
Color.clear
.preference(key: ViewWidthKey.self, value: $0.frame(in: .local).size.width)
})
.onPreferenceChange(ViewWidthKey.self) {
if $0 > self.width {
self.width = $0
}
}
.frame(width: width)
}
}
struct ViewWidthKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue = CGFloat.zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}