SwiftUI: выровнять HStack в VStack относительно центра подвида HStack?

#swiftui

Вопрос:

Поэтому у меня есть такая точка зрения:

 struct AlignmentTest: View {    @State var op:Double = 1    var body: some View {    VStack {    HStack {  Circle()  .fill(.green)  .opacity(op)  .frame(width: 50, height: 50)    Rectangle()  .fill(.red)  .frame(width:150, height: 50)  }    }  }  }  

Он рисует это:

введите описание изображения здесь

Учитывая один из комментариев, я делаю это редактирование:

Вопрос:

Как я могу сделать так, чтобы красный прямоугольник был центрирован (горизонтально) в VStack, а зеленый круг был прикреплен к левому краю красного прямоугольника?

Можно ли это сделать без жесткого кодирования чисел? Может быть, с направляющими для выравнивания?

Комментарии:

1. «Можно ли это сделать без жестких кодирующих чисел?» — У вас уже есть числа, жестко закодированные по ширине круга и прямоугольника. Вы думаете, что у вас там может быть что-то динамического размера? Я могу обновить свой ответ, чтобы использовать a GeometryReader , чтобы при необходимости ничего не было жестко закодировано, но если вы планируете уже иметь дочерние элементы, жестко закодированные по размеру, похоже, что вы также можете жестко закодировать выравнивание.

2. Я планирую, вероятно, применить масштабную анимацию к кругу…. и, в общем, я пытался понять, как сделать это без цифр, поэтому, если возможно, используйте направляющие для выравнивания. Мне кажется, это самый чистый способ. Во всяком случае, именно об этом я и думал.

3. Хорошо. Я обновил свой ответ-сейчас он немного надуман, потому что рамки явно заданы, но он будет вести себя так же и с динамическим контентом.

4. Но почему, как только вы зададите вопрос с жесткими значениями кода, затем измените цель на что-то другое? Мы потратили время на этот вопрос правильно? @zumzum

Ответ №1:

  1. Чтобы заставить круг прилипнуть к краю прямоугольника, явно объявите spacing значение 0 на HStack .
  2. Поскольку вам не нужны были жестко закодированные числа, я использовал PreferenceKeys для передачи значений ширины родителю.

Обратите внимание, что я не использую offset , потому что это может повлиять на взаимодействие дочерних элементов с пользователем (поскольку система считает, что они расположены по-разному).

(Я добавил фон и рамку, чтобы легко визуально увидеть центрирование-их, очевидно, можно было удалить)

  struct ContentView: View {    @State var op:Double = 1  @State private var rectangleWidth : CGFloat = 0  @State private var circleWidth : CGFloat = 0    var body: some View {  ZStack {  HStack(spacing: 0) {  Color.black  Color.white  }  VStack {  HStack(spacing: 0) {  Circle()  .fill(.green)  .opacity(op)  .frame(width: 50, height: 50)  .background(GeometryReader {  Color.clear.preference(key: CircleWidthPreferenceKey.self,  value: $0.frame(in: .local).size.width)  })    Rectangle()  .fill(.red)  .frame(width:150, height: 50)  .background(GeometryReader {  Color.clear.preference(key: RectangleWidthPreferenceKey.self,  value: $0.frame(in: .local).size.width)  })  }  .onPreferenceChange(RectangleWidthPreferenceKey.self) { pref in  self.rectangleWidth = pref  }  .onPreferenceChange(CircleWidthPreferenceKey.self) { pref in  self.circleWidth = pref  }  .border(Color.blue)  .alignmentGuide(HorizontalAlignment.center) { d in  circleWidth   rectangleWidth / 2  }  }  }  } }  struct CircleWidthPreferenceKey : PreferenceKey {  static var defaultValue: CGFloat { 0 }  static func reduce(value: inout Value, nextValue: () -gt; Value) {  value = nextValue()  } }  struct RectangleWidthPreferenceKey : PreferenceKey {  static var defaultValue: CGFloat { 0 }  static func reduce(value: inout Value, nextValue: () -gt; Value) {  value = nextValue()  } }  

введите описание изображения здесь

Ответ №2:

Вот способ для вас:

 struct AlignmentTest: View {    @State var op:Double = 1    private let rectangleSize: CGSize = CGSize(width: 150.0, height: 50.0)  private let circleSize: CGSize = CGSize(width: 50.0, height: 50.0)  private let padding: CGFloat = 10.0    var body: some View {    VStack {    HStack {     Rectangle()  .fill(Color.red)  .frame(width: rectangleSize.width, height: rectangleSize.height)  .overlay(    Circle()  .fill(Color.green)  .opacity(op)  .frame(width: circleSize.width, height: circleSize.height)  .offset(x: -(rectangleSize.width   circleSize.width)/2.0 - padding)      )  }      }  }  }  

Результат:

введите описание изображения здесь

Ответ №3:

Думаю, вы могли бы поместить что-нибудь такой же ширины, как круг справа от прямоугольника:

 VStack {  HStack {  Circle()  .fill(.green)  .opacity(op)  .frame(width: 50, height: 50)  Rectangle()  .fill(.red)  .frame(width:150, height: 50)  Rectangle()  .frame(width: 50, height: 0)    } }