Home > database >  Cannot make Type work while working with protocol
Cannot make Type work while working with protocol

Time:03-02

I just made a function for modifying views, the code builds itself, but when I am using it it complaints about Type issue related to protocol, it must be an easy fix, but I could not solve it.

struct CGSizePreferenceKey: PreferenceKey {
    
    static var defaultValue: CGSize { get { return CGSize() } }
    
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = nextValue() }
    
}

extension View {
    
    func onPreference<K: PreferenceKey>(key: K, value: K.Value, action: ((K.Value) -> Void)?) -> some View where K.Value: Equatable {
        return self
            .preference(key: K.self, value: value)
            .onPreferenceChange(K.self, perform: { newValue in action?(newValue)})
    }
}

Use case:

struct ContentTestView: View {
    
    @State private var size: CGSize = .zero
    
    var body: some View {
        
        Button("update") { size = CGSize(width: 100, height: 100) }
         .onPreference(key: CGSizePreferenceKey, value: size, action: { newValue in print(newValue) })
    }
}

Errors:

Instance method 'onPreference(key:value:action:)' requires that 'CGSizePreferenceKey.Type.Value' conform to 'Equatable'

Cannot convert value of type 'CGSize' to expected argument type 'CGSizePreferenceKey.Type.Value'

CodePudding user response:

First things first, you should change the definition of onPreference to accept a meta-type instead of an instance of K, the simple reason being that you never use that instance of K, SwiftUI doesn't ask for an instance of PreferenceKey in that context.

extension View {    
    func onPreference<K: PreferenceKey>(key: K.Type, value: K.Value, action: @escaping (K.Value) -> Void) -> some View where K.Value: Equatable {

I also took the liberty of removing the optionality of the action parameter, since it makes little sense to call onPreference without a callback.

Now, with the new definition in place, what's left is to update the call site to pass the meta-type:

.onPreference(key: CGSizePreferenceKey.self, ...

Note that a shorter solution would've been to just instantiate the preference at the call site:

.onPreference(key: CGSizePreferenceKey(), ...

, that would've make the compiler happy, however it wouldn't have been the right solution.

  • Related