Сделать ширину родительского вида шириной самого маленького потомка? - PullRequest
0 голосов
/ 06 ноября 2019

Как я могу сделать ширину родительского представления шириной самого маленького потомка?

Я могу узнать ширину дочернего представления, используя ответ на этот вопрос примерно так:

struct WidthPreferenceKey: PreferenceKey {
    static var defaultValue = CGFloat(0)

    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }

    typealias Value = CGFloat
}

struct TextGeometry: View {
    var body: some View {
        GeometryReader { geometry in
            return Rectangle().fill(Color.clear).preference(key: WidthPreferenceKey.self, value: geometry.size.width)
        }
    }
}

struct Content: View {

    @State var width1: CGFloat = 0
    @State var width2: CGFloat = 0

    var body: some View {
        VStack {
            Text("Short")
              .background(TextGeometry())
              .onPreferenceChange(WidthPreferenceKey.self, perform: { self.width1 = $0 })
            Text("Loooooooooooong")
              .background(TextGeometry())
              .onPreferenceChange(WidthPreferenceKey.self, perform: { self.width2 = $0 })
        }
        .frame(width: min(self.width1, self.width2)) // This is always 0
    }

}

Но VStack всегда имеет ширину 0. Этокак будто линия .frame(width: min(self.width1, self.width2)) вызывается до того, как задана ширина.

Есть идеи?

1 Ответ

2 голосов
/ 06 ноября 2019

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

По сути, VStack хочет максимально сжать, чтобы соответствовать размеру его содержимого. Аналогично, текстовые представления желают максимально сжать (урезать их содержимое), чтобы они соответствовали их родительскому представлению. Единственное жесткое требование, которое вы задали, это то, что начальный кадр VStack имеет ширину 0. Таким образом, происходит следующая последовательность:

  1. width1 и width2 инициализируются равными 0
  2. VStack устанавливает свою ширину на min(0, 0)
  3. Text Вид изнутри сжимается до ширины 0
  4. WidthPreferenceKey.self установлен на 0
  5. .onPreferenceChange устанавливает width1 и width2, которые уже равны 0

Все ограничения выполнены, и SwiftUI успешно останавливает макет.

Давайте изменим ваш код следующим образом:

  1. Сделайте WidthPreferenceKey.Value typealias до [CGFloat] вместо CGFloat. Таким образом, вы можете установить столько установщиков ключей предпочтения, сколько захотите, и они будут просто накапливаться в массив.
  2. Используйте один вызов .onPreferenceChange, который найдет минимум всех прочитанных значений, иустановите одно @State var width: CGFloat свойство
  3. Добавьте .fixedSize() к Text представлениям.

Примерно так:

struct WidthPreferenceKey: PreferenceKey {
    static var defaultValue = [CGFloat]()

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

    typealias Value = [CGFloat]
}

struct TextGeometry: View {
    var body: some View {
        GeometryReader { geometry in
            return Rectangle()
                .fill(Color.clear)
                .preference(key: WidthPreferenceKey.self,
                            value: [geometry.size.width])
        }
    }
}

struct Content: View {

    @State var width: CGFloat = 0

    var body: some View {
        VStack {
            Text("Short")
                .fixedSize()
                .background(TextGeometry())
            Text("Loooooooooooong")
                .fixedSize()
                .background(TextGeometry())
        }
        .frame(width: self.width)
        .clipped()
        .background(Color.red.opacity(0.5))
        .onPreferenceChange(WidthPreferenceKey.self) { preferences in
            print(preferences)
            self.width = preferences.min() ?? 0
        }
    }

}

Теперь происходит следующее:

  1. width устанавливается в 0
  2. VStack устанавливает ширину в 0
  3. Text представления расширяются снаружи VStack, поскольку мы дали разрешение с .fixedSize()
  4. WidthPreferenceKey.self установлено на [42.5, 144.5] (или что-то близкое)
  5. .onPreferenceChange устанавливает width на 42.5
  6. VStack устанавливает свою ширину 42.5, чтобы удовлетворить свой модификатор .frame().

Все ограничения выполнены, и SwiftUI останавливает макет. Обратите внимание, что .clipped() не позволяет отображать края длинного вида Text, даже если они технически находятся за пределами VStack.

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