Когда мне нужно реализовать содержимое `reduce`?

avatar
shingo.nakanishi
9 августа 2021 в 06:42
123
1
3

Используйте этот код в качестве ссылки.

Если вы закомментируете код и оставите содержимое пустым, а затем запустите его, произойдет то же самое.

struct SizePreferenceKey: PreferenceKey {
    typealias Value = CGSize
    static var defaultValue: Value = .zero

    static func reduce(value _: inout Value, nextValue: () -> Value) {
//        _ = nextValue() <-- comment out
    }
}

Я попробовал то же самое с этим кодом, и он работает нормально.

struct ViewOffsetKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
//        value += nextValue() <-- comment out
    }
}

Я должен записать его в conform protocol, но, похоже, это работает, даже если содержимое пустое. Почему это? Когда мне понадобится контент?

Другими словами, кажется, что onPreferenceChange работает нормально без обновления value. Почему это?

Источник
Sweeper
9 августа 2021 в 07:52
0

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

Ответы (1)

avatar
Asperi
9 августа 2021 в 07:42
3

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

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

Демо подготовлено с Xcode 13 / iOS 15

demo

extension CGSize {
    static func +=(lhs: inout CGSize, rhs: CGSize) {
        lhs.width += rhs.width
        lhs.height += rhs.height
    }
}

struct SizePreferenceKey: PreferenceKey {
    typealias Value = CGSize
    static var defaultValue: Value = .zero

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

struct TestPrefReduce: View {
    @State private var size: CGSize = .zero

    var body: some View {
        VStack {
            Text("Common size: [\(Int(size.width)) x \(Int(size.height))]")
            Divider()
            Text("Title").font(.title)
                .background(GeometryReader {
                    Color.clear.preference(key: SizePreferenceKey.self,
                        value: $0.frame(in: .local).size)
                     })
            Text("Hello, World!").font(.headline)
                .background(GeometryReader {
                    Color.clear.preference(key: SizePreferenceKey.self,
                        value: $0.frame(in: .local).size)
                     })
        }
        .onPreferenceChange(SizePreferenceKey.self) {
              self.size = $0
        }
    }
}
George
9 августа 2021 в 09:44
0

В чем разница между $0.size и $0.frame(in: .local).size? Я бы подумал, что это то же самое