Referencing initializer 'init(_:content:)' on 'ForEach' requires that 'Image' conform to 'Identifiable'

Hi, I'm doing a practise project about Image Filter. I would like to show a horizontal scrollView at the bottom of screen which showing all filtered images, but it displayed an error when using ForEach.

Error: Referencing initializer 'init(_:content:)' on 'ForEach' requires that 'Image' conform to 'Identifiable'

Can someone help me out before Christmas?

Happy holiday! Wish all of you have a wonderful Christmas!

ContentView

import CoreImage
import CoreImage.CIFilterBuiltins
import SwiftUI

struct ContentView: View {
  @State private var showingImagePicker = false
  @State private var image: Image?
  @State private var inputImage: UIImage?
  @State private var showingFilterView = false
  @State private var filterAmount = 0.5
  @State private var filters: [CIFilter] = [
    CIFilter.sepiaTone(),
    CIFilter.vignette(),
    CIFilter.discBlur(),
    CIFilter.pixellate(),
    CIFilter.crystallize(),
    CIFilter.twirlDistortion()
  ]
   
  let context = CIContext()
   
  var body: some View {
    ZStack {
      Image("background")
        .resizable()
        .scaledToFill()
        .ignoresSafeArea(.all)
       
      VStack {
        Button {
           showingImagePicker = true
        } label: {
          VStack {
            Image(systemName: "plus.square.fill.on.square.fill")
              .resizable()
              .frame(width: 40, height: 40)
            Text("Add photo")
              .font(.title3)
          }
          .foregroundColor(.white)
        }
         
      }
    }
    .preferredColorScheme(.dark)
    .sheet(isPresented: $showingImagePicker) {
      ImagePicker(image: $inputImage)
    }
    .onChange(of: inputImage) { _ in loadImage();loadFilter()}
    .navigate(to: FilterView(image: image), when: $showingFilterView)
  }
   
  func loadImage() {
    guard let inputImage = inputImage else {return}
    image = Image(uiImage: inputImage)
    showingFilterView = true
  }
   
  func loadFilter() {
    filters.forEach { (filters) in
      guard let inputImage = inputImage else { return }
      let beginImage = CIImage(image: inputImage)
      filters.setValue(beginImage, forKey: kCIInputImageKey)
      guard let outputImage = filters.outputImage else {return}
      let cgimg = context.createCGImage(outputImage, from: outputImage.extent)
      let uiImage = UIImage(cgImage: cgimg!)
      let filteredImage = Image(uiImage: uiImage)
       
      let filterView = FilterView()
      filterView.filteredImage.append(filteredImage)
    }
  }
}

extension View {
  /// Navigate to a new view.
  /// - Parameters:
  ///  - view: View to navigate to.
  ///  - binding: Only navigates when this condition is `true`.
  func navigate<NewView: View>(to view: NewView, when binding: Binding<Bool>) -> some View {
    NavigationView {
      ZStack {
        self
          //.navigationBarTitle("")
          //.navigationBarHidden(true)

        NavigationLink(
          destination: view,
            //.navigationBarTitle(""),
            //.navigationBarHidden(true),
          isActive: binding
        ) {
          EmptyView()
        }
      }
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

FilterView

import SwiftUI

struct FilterView: View{
   
  @State var image: Image?
  @State var filteredImage: [Image] = []
  var body: some View {
    NavigationView{
      ZStack{
        Image("background")
          .resizable()
          .scaledToFill()
          .ignoresSafeArea(.all)
         
        VStack {
          image?
            .resizable()
            .scaledToFit()
           
          ScrollView(.horizontal, showsIndicators: false) {
            HStack{
              ForEach(filteredImage) { image in
                Button {
                   
                } label: {
                  image
                }

                 
              }
            }
          }
           
        }
      }
    }
    .toolbar {
      ToolbarItem(placement: .navigationBarTrailing) {
        Button {
          save()
        } label: {
          Image(systemName: "square.and.arrow.down")
        }

      }
    }
  }
   
  func save() {
     
  }
}

struct FilterView_Previews: PreviewProvider {
  static var previews: some View {
    FilterView()
  }
}

Replies

As you can find in the error message, each element passed to ForEach needs to conform to Identifiable (or easily provide id to identify).

You should better think Image is not a good thing to hold in an Array and use with ForEach.

One possible solutions would be holding UIImage (or CGImage) in an Array:

Something like this:

struct FilterView: View{
    
    @State var image: Image?
    let filteredUIImage: [UIImage] //<-
    var body: some View {
        NavigationView{
            ZStack{
                //...
                VStack {
                    //...
                    ScrollView(.horizontal, showsIndicators: false) {
                        HStack{
                            ForEach(filteredUIImage, id: \.self) { uiImage in //<-
                                //...
                            }
                        }
                    }
                    
                }
            }
        }
        .toolbar {
            //...
        }
    }
    
    //...
}

And creating a local variable holding a view does not make sense.

You need to update your loadFilter():

struct ContentView: View {
    //...
    
    @State var filteredUIImage: [UIImage] = [] //<-
    
    //...
    
    var body: some View {
        
        //...
        .navigate(to: FilterView(image: image, filteredUIImage: filteredUIImage), when: $showingFilterView) //<-
    }
    
    //...
    
    func loadFilter() {
        filteredUIImage = []
        filters.forEach { (filters) in
            //...
            let uiImage = UIImage(cgImage: cgimg!)
            //let filteredImage = Image(uiImage: uiImage)

            //Creating a view as local variable does not make sense
//            let filterView = FilterView()
//            filterView.filteredImage.append(filteredImage)
            filteredUIImage.append(uiImage)
        }
    }
}

I'm not sure if this is the best solution for your purpose, generally having many images in an array would not be recommended.

  • Thank you so much

  • I tried UIImage and CGImage, neither one of them works

  • @Franklan010, sorry but not works is one of the least useful responses. Can you share your code which can reproduce the issue? Including what you get with the code and what you expect.

Add a Comment