Поведение SwiftUI .frame (высота: ноль)

avatar
Mikael Weiss
26 октября 2020 в 19:24
2106
3
7

Проблема

Я получаю сообщение о том, что "Неверный размер кадра (отрицательный или неконечный)".

struct CellStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .frame(width: .infinity, height: 56, alignment: .center)
            .padding(.horizontal, 16)
    }
}

Код работает нормально, и мне кажется, что он делает то, что должен, поэтому я не понимаю, почему у меня возникает проблема с потоком. Почему код будет работать без конечной ширины/высоты, что противоречит документам Apple? (см. раздел «Читать» ниже)

Исправление

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

struct CellStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .frame(height: 56, alignment: .center)
            .frame(maxWidth: .infinity)
            .padding(.horizontal, 16)
    }
}

Насколько я могу судить, у меня возникает проблема с потоком, потому что я говорю .frame, что мне нужна неконечная ширина, а ему нужна конечная ширина.

Другие вопросы о переполнении стека

Я обнаружил два вопроса, которые задают одно и то же:
iOS 14: неверный размер кадра (отрицательный или неконечный)

SwiftUI iOS14 GeometryReader Недопустимый размер кадра

В первом вопросе проблема с потоком имеет смысл, поскольку с учетом кода .frame(width: p.size.width - padding) нет гарантии, что p.size.width будет меньше, чем padding. Насколько я могу судить, это проблема, когда заданное значение может быть отрицательным.
Эта проблема, с которой я столкнулся, связана с тем, что заданное значение не является конечным, поэтому вопросы не связаны между собой.
На второй вопрос до сих пор нет ответа, и он немного расплывчатый, поэтому сейчас он мне не очень полезен. Возможно, позже кто-то ответит полезным ответом, но пока это бесполезно.

Читать

Глядя на документы Apple я нахожу это:

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

Тогда почему .frame(width: .infinity, height: 56, alignment: .center) вообще запускается?
Что Apple имеет в виду, говоря, что «результирующее представление предполагает изменение размеров этого представления в другом измерении»? Будет ли он всегда предполагать, что это представление является «выталкивающим», что дает тот же результат, что и .infinity?

Я проверил это и запустил этот код:

struct CellStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .frame(height: 56, alignment: .center)
            .padding(.horizontal, 16)
    }
} 

Кажется, это дает тот же результат, так что теперь мне любопытно, был ли когда-нибудь случай, когда быстрое "предполагание" ширины приведет к поведению, отличному от .infinity?

Последний вопрос

В чем разница в поведении между .frame(width: .infinity) и .frame(width: nil) (эквивалент выделения либо ширины, либо высоты).

функция кадра (ширина: CGFloat? = nil, высота: CGFloat? = nil, выравнивание: Alignment = .center) -> некоторый вид

Документы Apple

Спасибо за помощь!

Источник

Ответы (3)

avatar
Mikael Weiss
26 октября 2020 в 22:32
2

Что делает .frame(width: nil)

В основном .frame примет заданные значения и вернет другое представление, которое будет обновлено с учетом введенных вами значений.
Если вы опустите один из параметров или установите его равным нулю, он примет соответствующее значение ширины/высоты предыдущего представления.

Код теста:

struct ContentView: View {
    @State var keyValue = ""
    var body: some View {
        VStack(spacing: 10) {
            Text("Something")
                .background(Color.blue)
            
            Text("Something")
                .frame(width: 300, height: nil, alignment: .center)
                .background(Color.blue)
            
            Text("Something")
                .frame(width: nil, height: 200, alignment: .center)
                .background(Color.blue)
            
            Text("Something")
                .frame(width: nil, height: nil, alignment: .center)
                .background(Color.blue)
        }
    }
}

Image of the result of the above code

Почему .frame(width: .infinity) все еще работает

.infinity (Apple doc on .infinity) имеет тип Float, поэтому .frame никак не может не исключить его, но он все же может создавать проблемы. (Таким образом вопрос потока)

Спасибо Марку Мойкенсу за помощь в получении этого ответа!

Примечание: .frame(maxWidth: .infinity) вместо .frame(width: .infinity)

avatar
Badal Shah
15 декабря 2021 в 17:27
0

Попытка 1:- Применение .frame(width: nil, height: 200, alignment: .center) у меня не работает.

Попытка 2: - Применение .frame(width: .infinity, height: 200, alignment: .center) у меня не работает.

Результат с попыткой 1 и попыткой 2 ,

enter image description here


Попытка 3:- (Успех) Применение .frame(minWidth: 0, maxWidth: .infinity) У меня работает.

Результат попытки 3

enter image description here

avatar
Asperi
26 октября 2020 в 19:43
2

Поскольку это задокументированный контракт интерфейса:

/// - Parameters:
///   - width: A fixed width for the resulting view. If `width` is `nil`,
///     the resulting view assumes this view's sizing behavior.
///   - height: A fixed height for the resulting view. If `height` is `nil`,
///     the resulting view assumes this view's sizing behavior.
///   - alignment: The alignment of this view inside the resulting view.
///     `alignment` applies if this view is smaller than the size given by
///     the resulting frame.
///
/// - Returns: A view with fixed dimensions of `width` and `height`, for the
///   parameters that are non-`nil`.
@inlinable public func frame(width: CGFloat? = nil, height: CGFloat? = nil, alignment: Alignment = .center) -> some View

в то время как тип модификатора с аргументами min/max допускает гибкие ограничения (здесь не копируется — читайте в сгенерированном модуле SwiftUI).

Mikael Weiss
26 октября 2020 в 21:19
0

Если вы прочтете, что я сказал после «Немного чтения», вы увидите, что я это понимаю. Что меня смущает, так это то, почему указание .infinity в качестве ширины или высоты все равно будет работать, а затем, если оставить параметры ширины или высоты, всегда будет выводиться, что ширина/высота будет «выталкивать», например .infinity