SwiftUI: различное поведение выравнивания контейнеров в GeometryReader

А Textэлемент внутри ZStack(alignment: .bottom)отображается в нижней части контейнера (как и ожидалось). а если так же ZStackнаходится внутри GeometryReader, выравнивание больше не ведет себя так же.

Этот код:

      var body: some View {
  ZStack(alignment: .bottom) {
    VStack {
      Text("Outside geo")
    }
  
    GeometryReader { geo in
      ZStack(alignment: .bottom) {
        VStack {
          Text("Inside geo")
        }
      }.frame(maxWidth: .infinity, maxHeight: .infinity)
    }
  }.frame(maxWidth: .infinity, maxHeight: .infinity)
}

производит этот результат:

Если бы я хотел, чтобы "внутренняя география" тоже располагалась внизу, мне пришлось бы добавить alignment: .bottomвнутри его родительского модификатора ZStack .frame(...).

Каковы предпосылки такого поведения? Почему я могу использовать ZStack(alignment: ...)в одном случае, но придется использовать ZStack {...}.frame(alignment: ...)в другом для достижения того же результата?

2 ответа

Это может показаться странным, но текст «Inside geo» в файле правильно выровнен по нижней части — проблема в том, что ваш текст не выровнен по нижней части файла .

Стеки занимают минимально необходимое пространство, в то время как имеет противоположное поведение. Таким образом, элемент выровнен по нижнему краю верхнего уровня, но занимает весь экран, а внутренний уровень располагается по центру внутри верхнего уровня. GeometryReader.

Попробуйте поставить .background(someColor)на каждом представлении, и вы увидите, где они расположены.

Если вы хотите, чтобы «Внутренняя гео» находилась внизу экрана, вам нужно указать ZStackчтобы оказаться внизу с alignmentпараметр - вот так:

          var body: some View {
        ZStack(alignment: .bottom) {
            VStack {
                Text("Outside geo")
            }
            
            GeometryReader { geo in
                ZStack(alignment: .bottom) {
                    VStack {
                        Text("Inside geo")
                    }
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) // Here you add the alignment parameter
            }

        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

После экспериментов я теперь знаю, что вызывает такое поведение, но сначала это казалось неожиданным.

При установке максимального размера .frame ZStack на .infinity (чтобы растянуть его), его «внутренний размер»/ содержимое по-прежнему остается «сжатым», а не растянутым. Это означает, что содержимое контейнера не всегда обязательно использует все пространство, которое могло бы быть.

Возьмите этот пример:

      ZStack(alignment: .bottom) {
  Color.red
    .frame(width: 50, height: 50)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue)

Это приводит к совершенно другому размещению красного прямоугольника, чем при добавлении второго представления, которое раздвигает внутренние границы контейнера .

      ZStack(alignment: .bottom) {
  Color.red
    .frame(width: 50, height: 50)
  
  Color.clear
    .frame(maxHeight: .infinity)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue)

Рамка чистого цвета теперь раздвигает границы своих родителей, поскольку ее высота пытается увеличиться настолько, насколько это возможно. GeometryReader имеет ту же природу, так как он всегда пытается заполнить своего родителя.

Теперь красная коробка больше не обнимается и имеет все пространство (вызванное чистым цветом) для свободного перемещения (вниз).

Вот два результата:

Заключение

Параметр решает, где обтянутая «внутренняя рама» контейнера размещается внутри полной рамы контейнера. В Параметр Контейнера определяет, где его содержимое размещается внутри его «внутренней рамки» (которую можно обнять).

Другие вопросы по тегам