Обнимите подпредставления в SwiftUI - PullRequest
1 голос
/ 30 июня 2019

Дейв Абрахамс объяснил некоторые механизмы компоновки SwiftUI в своем выступлении на WWDC19 о пользовательских представлениях, но он не учел некоторые моменты, и у меня возникли проблемы с определением размеров моих представлений.

Есть ли способ, чтобы View мог сказать своему контейнеру, что он не предъявляет никаких требований к пространству, но будет использовать все пространство, которое ему предоставляется? Еще один способ сказать, что контейнер должен обнять свои подпредставления.

Конкретный пример, я хочу что-то вроде c:

Some Texts inside a VStack

Если у вас есть Text s внутри VStack, как в а), VStack примет его ширину для самого широкого подпредставления.

Если вы добавите Rectangle, хотя, как и в b), он будет расширяться настолько, насколько это возможно, пока VStack не заполнит его контейнер.

Это означает, что Text s и Rectangle s относятся к разным категориям, когда дело доходит до макета, Text имеет фиксированный размер, а Rectangle является жадным. Но как я могу передать это своему контейнеру, если я делаю свой собственный View?

Результат, которого я на самом деле хочу достичь, это c). VStack должен игнорировать Rectangle (или мой пользовательский вид), когда он определяет его размер, а затем, как только он это сделает, затем он должен сообщить Rectangle или моему пользовательскому представлению, сколько места он может иметь.

Учитывая, что SwiftUI, кажется, размещает снизу вверх, возможно, это невозможно, но кажется, что для этого должен быть some способ.

Ответы [ 2 ]

2 голосов
/ 30 июня 2019

Для этого нет модификатора (AFAIK), так что вот мой подход. Если вы собираетесь использовать это слишком часто, возможно, стоит создать свой собственный модификатор.

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

Тем временем вы можете использовать приведенный ниже код для выполнения того, что вы ищете.

import SwiftUI

struct MyRectPreference: PreferenceKey {
    typealias Value = [CGRect]

    static var defaultValue: [CGRect] = []

    static func reduce(value: inout [CGRect], nextValue: () -> [CGRect]) {
        value.append(contentsOf: nextValue())
    }
}

struct ContentView : View {
    @State private var widestText: CGFloat = 0

    var body: some View {
        VStack {
            Text("Hello").background(RectGetter())
            Text("Wonderful World!").background(RectGetter())
            Rectangle().fill(Color.blue).frame(width: widestText, height: 30)
            }.onPreferenceChange(MyRectPreference.self, perform: { prefs in
                for p in prefs {
                    self.widestText = max(self.widestText, p.size.width)
                }
            })
    }
}

struct RectGetter: View {

    var body: some View {
        GeometryReader { geometry in
            Rectangle()
                .fill(Color.clear)
                .preference(key: MyRectPreference.self, value: [geometry.frame(in: .global)])
        }
    }
}
0 голосов
/ 02 июля 2019

Так что я действительно нашел способ сделать это. Сначала я попытался поместить Spacers вокруг представлений в различных конфигурациях, чтобы попытаться собрать их вместе, но это не сработало. Тогда я понял, что, возможно, смогу использовать модификатор .background, и это действительно сработало. Похоже, что сначала собственное представление вычисляет его размер, а затем просто принимает его в качестве фрейма, а это именно то, что мне нужно.

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

var body: some View {
    VStack(spacing: 10) {
        Text("Short").background(Color.green)
        Text("A longer text").background(Color.green)
        Text("Dummy").opacity(0)
    }
    .background(backgroundView)
    .background(Color.red)
    .padding()
    .background(Color.blue)
}

var backgroundView: some View {
    VStack(spacing: 10) {
        Spacer()
        Spacer()
        Rectangle().fill(Color.yellow)
    }
}

Синий вид и все цветные фоны, конечно, просто для того, чтобы их было легче увидеть. Этот код производит это:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...