SwiftUI / add photos in circle

Viewed 744

When I put a photo as seen in the picture, the same photo comes in 3 circles.

I want to add separate photos to each circle, so I add them from the camera or gallery with the selector I created. However, the code I made may be wrong, can you show the correct logic? I don't have enough information to correct it sorry.

enter image description here

struct SendPicture: View {
    @State private var image: Image? = Image("plus")
    @State private var shouldPresentImagePicker = false
    @State private var shouldPresentActionScheet = false
    @State private var shouldPresentCamera = false
    
    var body: some View {
        GeometryReader{ geometry in
            VStack{
                HStack{
                    Text("Picture")
                    .font(.system(size: 60))
                    .frame(width:geometry.size.width, height: geometry.size.height / 10)
                    .foregroundColor(.init(red: 45/255, green: 0/255, blue: 112/255))
                    Spacer().frame(height: geometry.size.height / 5)
                }
                
                VStack{
                    Text("Hi").frame(width:geometry.size.width, height: geometry.size.height / 10)
                    
                }
                VStack{
                    Image("AstroGirl").resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: geometry.size.width,height: geometry.size.height / 3)
                    
                }
                HStack{
                    image!
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: geometry.size.width / 5, height: geometry.size.height / 10)
                    .clipShape(Circle())
                    .overlay(Circle().stroke(Color.yellow, lineWidth: 4))
                    .shadow(radius: 10)
                    .onTapGesture { self.shouldPresentActionScheet = true }
                    .sheet(isPresented: $shouldPresentImagePicker) {
                        SUImagePickerView(sourceType: self.shouldPresentCamera ? .camera : .photoLibrary, image: self.$image, isPresented: self.$shouldPresentImagePicker)
                    }.actionSheet(isPresented: $shouldPresentActionScheet) { () -> ActionSheet in
                        ActionSheet(title: Text("Choose mode"), message: Text("Please choose your preferred mode to set your profile image"), buttons: [ActionSheet.Button.default(Text("Camera"), action: {
                            self.shouldPresentImagePicker = true
                            self.shouldPresentCamera = true
                        }), ActionSheet.Button.default(Text("Photo Library"), action: {
                            self.shouldPresentImagePicker = true
                            self.shouldPresentCamera = false
                        }), ActionSheet.Button.cancel()])
                    }
                    image!
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: geometry.size.width / 5, height: geometry.size.height / 10)
                    .clipShape(Circle())
                    .overlay(Circle().stroke(Color.yellow, lineWidth: 4))
                    .shadow(radius: 10)
                    .onTapGesture { self.shouldPresentActionScheet = true }
                    .sheet(isPresented: $shouldPresentImagePicker) {
                        SUImagePickerView(sourceType: self.shouldPresentCamera ? .camera : .photoLibrary, image: self.$image, isPresented: self.$shouldPresentImagePicker)
                    }.actionSheet(isPresented: $shouldPresentActionScheet) { () -> ActionSheet in
                        ActionSheet(title: Text("Choose mode"), message: Text("Please choose your preferred mode to set your profile image"), buttons: [ActionSheet.Button.default(Text("Camera"), action: {
                            self.shouldPresentImagePicker = true
                            self.shouldPresentCamera = true
                        }), ActionSheet.Button.default(Text("Photo Library"), action: {
                            self.shouldPresentImagePicker = true
                            self.shouldPresentCamera = false
                        }), ActionSheet.Button.cancel()])
                    }
                    image!
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: geometry.size.width / 5, height: geometry.size.height / 10)
                    .clipShape(Circle())
                    .overlay(Circle().stroke(Color.yellow, lineWidth: 4))
                    .shadow(radius: 10)
                    .onTapGesture { self.shouldPresentActionScheet = true }
                    .sheet(isPresented: $shouldPresentImagePicker) {
                        SUImagePickerView(sourceType: self.shouldPresentCamera ? .camera : .photoLibrary, image: self.$image, isPresented: self.$shouldPresentImagePicker)
                    }.actionSheet(isPresented: $shouldPresentActionScheet) { () -> ActionSheet in
                        ActionSheet(title: Text("Choose mode"), message: Text("Please choose your preferred mode to set your profile image"), buttons: [ActionSheet.Button.default(Text("Camera"), action: {
                            self.shouldPresentImagePicker = true
                            self.shouldPresentCamera = true
                        }), ActionSheet.Button.default(Text("Photo Library"), action: {
                            self.shouldPresentImagePicker = true
                            self.shouldPresentCamera = false
                        }), ActionSheet.Button.cancel()])
                    }
                }
                Spacer().frame(height: geometry.size.height / 10)
                VStack{
                    Button(action: {
                        
                    }, label: {
                        Text("Giriş Yap")
                        .frame(width: geometry.size.width / 3, height: 50)
                        .padding(10)
                        .font(Font.system(size: 30, weight: .medium, design: .serif))
                        .foregroundColor(.white)
                        .background(RoundedRectangle(cornerRadius: 30))
                        .foregroundColor(.init(red: 45/255, green: 0/255, blue: 112/255))
                        
                    })
                }
            }
        }
    }
}
import SwiftUI
import UIKit

struct SUImagePickerView: UIViewControllerRepresentable {
    
    var sourceType: UIImagePickerController.SourceType = .photoLibrary
    @Binding var image: Image?
    @Binding var isPresented: Bool
    
    func makeCoordinator() -> ImagePickerViewCoordinator {
        return ImagePickerViewCoordinator(image: $image, isPresented: $isPresented)
    }
    
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let pickerController = UIImagePickerController()
        pickerController.sourceType = sourceType
        pickerController.delegate = context.coordinator
        return pickerController
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
        // Nothing to update here
    }
}

class ImagePickerViewCoordinator: NSObject, UINavigationControllerDelegate,
UIImagePickerControllerDelegate {
    
    @Binding var image: Image?
    @Binding var isPresented: Bool
    
    init(image: Binding<Image?>, isPresented: Binding<Bool>) {
        self._image = image
        self._isPresented = isPresented
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            self.image = Image(uiImage: image)
        }
        self.isPresented = false
    }
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        self.isPresented = false
    }
}
2 Answers

I'm just learning SwiftUI, but it seems to me you are sharing the same @State variables for all 3 images. I bet all 3 closures are firing and installing the picked image in all 3 image views.

Try setting up separate state variables for each icon. I'd also write a shared functions for setting up the picker and sheet and call them from each icon's tap action. That would avoid all that duplicate code.

If you upload a working project or SwiftUI playground to Github an post a link we can try to show you how to fix it. (I am at a disadvantage since I only started studying SwiftUI last week, but I've been doing iOS development for quite a few years now.)

As Duncan C observed, you're using the same @State property for all your image views.

A possible solution is to extract the repeating code to a new view:

struct IconView: View {
    @State private var image: Image?
    @State private var shouldPresentImagePicker = false
    @State private var shouldPresentActionScheet = false
    @State private var shouldPresentCamera = false

    var imageView: Image {
        image ?? Image("plus")
    }

    var body: some View {
        imageView
            .resizable()
            .aspectRatio(contentMode: .fill)
            .clipShape(Circle())
            .overlay(Circle().stroke(Color.yellow, lineWidth: 4))
            .onTapGesture { self.shouldPresentActionScheet = true }
            .sheet(isPresented: $shouldPresentImagePicker) {
                SUImagePickerView(sourceType: self.shouldPresentCamera ? .camera : .photoLibrary, image: self.$image, isPresented: self.$shouldPresentImagePicker)
            }
            .actionSheet(isPresented: $shouldPresentActionScheet) { () -> ActionSheet in
                ActionSheet(title: Text("Choose mode"), message: Text("Please choose your preferred mode to set your profile image"), buttons: [ActionSheet.Button.default(Text("Camera"), action: {
                    self.shouldPresentImagePicker = true
                    self.shouldPresentCamera = true
                }), ActionSheet.Button.default(Text("Photo Library"), action: {
                    self.shouldPresentImagePicker = true
                    self.shouldPresentCamera = false
                }), ActionSheet.Button.cancel()])
            }
    }
}

and then use this view in your HStack:

struct SendPicture: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                ...
                HStack {
                    ForEach(0 ..< 3) { _ in
                        IconView()
                            .frame(width: geometry.size.width / 5, height: geometry.size.height / 10)
                            .shadow(radius: 10)
                    }
                }
                ...
            }
        }
    }
}

The whole SendPicture view would look like this:

struct SendPicture: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                HStack {
                    Text("Picture")
                        .font(.system(size: 60))
                        .frame(width: geometry.size.width, height: geometry.size.height / 10)
                        .foregroundColor(.init(red: 45 / 255, green: 0 / 255, blue: 112 / 255))
                    Spacer().frame(height: geometry.size.height / 5)
                }

                VStack {
                    Text("Hi").frame(width: geometry.size.width, height: geometry.size.height / 10)
                }
                VStack {
                    Image("AstroGirl").resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: geometry.size.width, height: geometry.size.height / 3)
                }
                // This `HStack` is changed
                HStack {
                    ForEach(0 ..< 3) { _ in
                        IconView()
                            .frame(width: geometry.size.width / 5, height: geometry.size.height / 10)
                            .shadow(radius: 10)
                    }
                }
                Spacer().frame(height: geometry.size.height / 10)
                VStack {
                    Button(action: {}, label: {
                        Text("Giriş Yap")
                            .frame(width: geometry.size.width / 3, height: 50)
                            .padding(10)
                            .font(Font.system(size: 30, weight: .medium, design: .serif))
                            .foregroundColor(.white)
                            .background(RoundedRectangle(cornerRadius: 30))
                            .foregroundColor(.init(red: 45 / 255, green: 0 / 255, blue: 112 / 255))

                    })
                }
            }
        }
    }
}
Related