SwiftUI: метод PreferenceKey Reduce никогда не вызывается

avatar
BlueskyMed
20 марта 2020 в 22:30
1393
1
3

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

вот код:

struct LittleTapPrefKey: PreferenceKey {
    typealias Value = String
    static var defaultValue: Value = "A0"
    static func reduce(value: inout Value, nextValue: () -> String) {
        print("Inside Little Reduce \(value)and NextVal =  \(nextValue())")
        value = nextValue()
    }
}

вот вид ячейки, где при касании экземпляра GlyphCell в QGrid устанавливается модификатор Pref:

struct GlyphCell: View {

    var glyph:Glyph
    @State private var selected = false   // << track own selection

    var body: some View {
        ZStack {
            Text("\(glyph.Hieroglyph)")
                .lineLimit(1)
                .padding(.all, 12.0)
                .font(.system(size: 40.0))
                .background(Color.yellow)
            Text("\(glyph.Gardiner)")
                .offset(x: 12, y: 28)
                .foregroundColor(.blue)
        }.onTapGesture {
            print("Tap on \(self.glyph.Gardiner)")
            self.selected.toggle()
        }
        .cornerRadius(6.0)
        .frame(width: 90.0, height: 100.0, alignment: .center)
        .previewLayout(.sizeThatFits)
        .background(Group {
            if self.selected {
                Color.clear
                    .preference(key: LittleTapPrefKey.self, value: self.glyph.Gardiner)
            } // end selected test
        }   // end  group
      )     // end .background
    }       // end cell body view
}

с точкой останова, установленной в .pref(key:...), точка останова срабатывает каждый раз, когда нажимается ячейка, и значение self.glyph.Gardiner в этой точке правильное.

Точки останова или оператор тестовой печати в функции "reduce" структуры ключа предпочтения никогда не запускаются. Поскольку .onPrefChange никогда не запускается, я должен предположить, что значение никогда не изменяется, но у меня нет возможности напрямую просмотреть значение Pref.

Что происходит?

Источник

Ответы (1)

avatar
Asperi
21 марта 2020 в 05:17
2

reduce вызывается, когда вы устанавливаете один ключ предпочтения в одной иерархии представлений несколько раз, поэтому SwiftUI дает вам возможность каким-то образом их комбинировать, например:

.previewLayout(.sizeThatFits)
.preference(key: LittleTapPrefKey.self, value: value1) // 1st time
.background(Group {
    if self.selected {
        Color.clear
            .preference(key: LittleTapPrefKey.self, value: value2) // 2nd time
    }
}

в вашем случае у вас есть только один установщик значений, поэтому он просто установлен, и модификатор обратного вызова .onPreferenceChange(LittleTapPrefKey.self) работает как положено.

Протестировано с Xcode 11.4 / iOS 13.4

BlueskyMed
21 марта 2020 в 13:53
0

Я знал функцию сокращения, но не знал, насколько она умна с точки зрения запуска обновления сохраненного местоположения ключа. В моем случае кажется, что оператор set key в блоке self.selected выполняется, и глиф был правильным, но без обратного вызова. Вы предлагаете «первый вызов» ключа предпочтения с (мусорным) значением, а затем устанавливаете ключ с желаемым значением. Таким образом, он дважды меняет настройку при вызове представления ячейки. Я не понимаю, почему для этого должны требоваться две настройки, но вы были правы с другими вашими предложениями, поэтому я попробую это сегодня утром! Еще раз спасибо Аспери!!!!

Asperi
21 марта 2020 в 13:59
0

@BlueskyMed, это было не предложение, а демонстрация случая, когда сокращение сработает. Конечно, вы не должны устанавливать pref дважды.

BlueskyMed
21 марта 2020 в 14:18
0

Ах! Тем не менее, после начального раунда настройки QGrid обратный вызов в родительском элементе никогда не вызывается после выбора ячейки. Это любопытная аномалия! Я добавил onPrefChange в родительское представление родительского элемента QGrid, и он также не срабатывает. Интересно то, что теперь, когда запускается сокращение, NextVal в операторе печати всегда является «мусорным» значением из добавленного setPref, а не значением внутри блока isSelected!

BlueskyMed
21 марта 2020 в 14:34
0

Я добавил отладчик PO stmt внутрь сокращения и вижу, что для каждого нажатия сокращение вызывается более 20 раз! Сначала с правильными значениями, потом со значениями по умолчанию! Вернуться к доске для рисования!