Search code examples
swiftuikitswiftuicombine

SwiftUI = ObservableObject as Choice of Class


Using SwiftUI I want to press a button and have it switch the class which is used to filter an image.

In SwiftUI, the button would do something like what follows:

@ObservedObject var currentFilter = FilterChoice()
...
var body: some View {..
  Button(action:{
    print("clicked")
    var newFilter = Luminance()
    self.currentFilter = newFilter
  }) {
     Text("Switch to Luminance Filter")
  }
}

There is an ObservableObject:

class FilterChoice: ObservableObject {  
    @Published var filter = Luminance()
}

Which is consumed by a UIViewRepresentable:

struct FilteredPhotoView: UIViewRepresentable {
  @ObservedObject var currentFilter = FilterChoice()

  func makeUIView(context: Context) -> UIView {
     ...
     // Code works and pulls correct filter but can not be changed
     let className = currentFilter.filter
     let filteredImage = testImage.filterWithOperation(className)
     ...   
  }...

Currently, FilteredPhotoView is properly returning the filtered image.

But how can ObservedObject be used to change a CLASS?

In other words, the ObservedObject sets the class correctly here:

class FilterChoice: ObservableObject {
   @Published var filter = Luminance()    
}

But how can this ObservableObject be changed so that the class can be changed in SwiftUI? For example, I want to click a button and the filter should be changed to another class (for example:

new filter = ColorInversion()

I think I understand how ObservableObjects work but I can't get it to work as a change of class rather than something simple like a string value.


Solution

  • What you actually need is some generics.

    Declare a protocol like this:

    protocol ImageFilter {
    
        func apply(to image: UIImage) // for example
    }
    

    Declare here any methods or properties that all your filters will share and that will be used by FilterChoice. Next declare all your filters as conforming to the protocol:

    class Luminance: ImageFilter {
        // implement all the methods and properties declared in the protocol
        // for every filter
    }
    

    Next declare your @Published filter to conform to that protocol

    class FilterChoice: ObservableObject {
        @Published var filter: ImageFilter
    
        public init(filter: ImageFilter) {
            self.filter = filter
        }
    
        // etc.
    }
    

    You will be able to change the filters used by @Published.