skip to Main Content

I have SwiftUI views appearing in my UIKit application based on data found there. What I would like to do is change the SwiftUI view based on a button tap in UIKit just to test. I am not great with bindings yet. Right now in my button code I am not sure what to do to affect change (i.e. highlight the second item).

Here is my current setup:

struct PresetBank: Identifiable
{
    var id: UUID
    let index: Int
    var title: String
    var iconString: String
    var presetsCount: Int
    var isSelected: Bool = false
}

class PresetStore: ObservableObject
{
    @Published var arrayOfPresets: [PresetBank]
    init(arrayOfPresets: [PresetBank]) {
        self.arrayOfPresets = arrayOfPresets
    }
    public func selectBank(index: Int) {
        print(index)
        // How can I change the PresetBank items isSelected
        // in order to update their UI? I can loop through
        // and change the prop, but it does nothing.
        for i in 0..<arrayOfPresets.count - 1 {
            arrayOfPresets[i].isSelected = false
        }
        arrayOfPresets[index].isSelected = true
        // The properties change, but the UI does not update.
        // I am missing something here.
    }
}

struct PresetBankView: View
{
    var bank: PresetBank
    @State var isSelected: Bool
    
    var body: some View
    {
        ZStack
        {
            VStack {
                Text(bank.title)
                    .multilineTextAlignment(.center)
                    .fontWeight(.medium)
                    .foregroundColor(bank.isSelected == true ? .white : .black)
                    .font(.system(size: 12))
                    .minimumScaleFactor(0.5)
                    .lineLimit(2)
                    .padding(.top, 10)
                    .padding(.leading, 5)
                    .padding(.trailing, 5)
                Spacer()
                Text("(bank.presetsCount)")
                    .multilineTextAlignment(.center)
                    .font(.system(size: 10))
                    .foregroundColor(bank.isSelected == true ? .white : .black)
                    .padding(.bottom, 10)
            }
            ZStack {
                    Image(systemName: bank.iconString)
                        .font(.system(size: 26))
                        .foregroundColor(bank.isSelected == true ? .white : .black)
               
                
            }.padding(.top, 10)
        }
        .frame(width: 100, height: 100)
        .background {
            RoundedRectangle(cornerRadius: 8)
               .aspectRatio(contentMode: .fill)
               .foregroundColor(bank.isSelected == true ? .black : .white)
               .shadow(color: .black.opacity(0.3), radius: 5, x: 0, y: 0)
               .overlay(
                    RoundedRectangle(cornerRadius: 8)
                        .stroke(.gray, lineWidth: 2)
                )
        }
        Spacer()
            .frame(width: 15)
    }
}

struct ContentView: View
{
    @State var store: PresetStore
    var body: some View
    {
        ScrollView(.horizontal)
        {
            HStack {
                
                // Pull in and use the data passed in from UIKit.
                
                ForEach(store.arrayOfPresets) { bank in
                    PresetBankView(bank: bank, isSelected: bank.isSelected)
                }
            }
            .padding()
        }
    }
}
...
class ViewController: UIViewController
{
    var thisStore: PresetStore!
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        let arrayOfPresets:[PresetBank] = []
        thisStore = PresetStore(arrayOfPresets: arrayOfPresets)
        
        let p1: PresetBank = PresetBank(id: UUID(), index: 0, title: "Default", iconString: "music.note.house", presetsCount: 5, isSelected: true)
        let p2: PresetBank = PresetBank(id: UUID(), index: 1, title: "Relaxation", iconString: "figure.mind.and.body", presetsCount: 2)
        let p3: PresetBank = PresetBank(id: UUID(), index: 2, title: "Dinner Party on Sunday Afternoon", iconString: "wineglass.fill", presetsCount: 3)
        let p4: PresetBank = PresetBank(id: UUID(), index: 3, title: "Workout", iconString: "figure.run", presetsCount: 5)
        let p5: PresetBank = PresetBank(id: UUID(), index: 4, title: "Sleepy Time", iconString: "powersleep", presetsCount: 2)
        
        thisStore.arrayOfPresets.append(p1)
        thisStore.arrayOfPresets.append(p2)
        thisStore.arrayOfPresets.append(p3)
        thisStore.arrayOfPresets.append(p4)
        thisStore.arrayOfPresets.append(p5)
        
        let contentView = UIHostingController(rootView: ContentView(store: thisStore))
        addChild(contentView)
        view.addSubview(contentView.view)
        contentView.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 200)
    }
    
    @IBAction func testSelect(_ sender: UIButton) {
        thisStore.selectBank(index: 1)

    }
}

So you can see in the selectBank method I want to loop through all the PresetBank and change their isSelected to false, and then set the one by index to true – and have the views update. I am unclear as to how to achieve that at the moment.

2

Answers


  1. Chosen as BEST ANSWER

    I think this solves things. Here is my updated code:

    class ViewController: UIViewController
    {
        var thisStore: PresetStore!
        
        override func viewDidLoad()
        {
            super.viewDidLoad()
            
            let arrayOfPresets:[PresetBank] = []
            thisStore = PresetStore(arrayOfPresets: arrayOfPresets)
            
            let p1: PresetBank = PresetBank(id: UUID(), index: 0, title: "Default", iconString: "music.note.house", presetsCount: 5, isSelected: true)
            let p2: PresetBank = PresetBank(id: UUID(), index: 1, title: "Relaxation", iconString: "figure.mind.and.body", presetsCount: 2, isSelected: false)
            let p3: PresetBank = PresetBank(id: UUID(), index: 2, title: "Dinner Party on Sunday Afternoon", iconString: "wineglass.fill", presetsCount: 3, isSelected: false)
            let p4: PresetBank = PresetBank(id: UUID(), index: 3, title: "Workout", iconString: "figure.run", presetsCount: 5, isSelected: false)
            let p5: PresetBank = PresetBank(id: UUID(), index: 4, title: "Sleepy Time", iconString: "powersleep", presetsCount: 2, isSelected: false)
            
            thisStore.arrayOfPresets.append(p1)
            thisStore.arrayOfPresets.append(p2)
            thisStore.arrayOfPresets.append(p3)
            thisStore.arrayOfPresets.append(p4)
            thisStore.arrayOfPresets.append(p5)
            
            let contentView = UIHostingController(rootView: ContentView(store: thisStore))
            addChild(contentView)
            view.addSubview(contentView.view)
            contentView.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 200)
        }
        
        @IBAction func testSelect(_ sender: UIButton) {
            thisStore.selectBank(index: sender.tag)
        }
    }
    
    class PresetBank: Identifiable, ObservableObject
    {
        var id: UUID = UUID()
        let index: Int
        var title: String
        var iconString: String
        var presetsCount: Int
        @Published var isSelected: Bool = false
        init(id:UUID, index:Int, title:String, iconString:String, presetsCount:Int, isSelected:Bool){
            self.id = id
            self.index = index
            self.title = title
            self.iconString = iconString
            self.presetsCount = presetsCount
            self.isSelected = isSelected
        }
    }
    
    class PresetStore: ObservableObject
    {
        @Published var arrayOfPresets: [PresetBank]
        init(arrayOfPresets: [PresetBank]) {
            self.arrayOfPresets = arrayOfPresets
        }
        public func selectBank(index: Int) {
            print(index)
            // How can I change the PresetBank items isSelected
            // in order to update their UI? I can loop through
            // and change the prop, but it does nothing.
            for i in 0..<arrayOfPresets.count - 1 {
                arrayOfPresets[i].isSelected = false
            }
            arrayOfPresets[index].isSelected = true
            print(arrayOfPresets)
            // Above works, but the change doesn't affect the UI.
        }
    }
    
    struct PresetBankView: View
    {
        @ObservedObject var bank: PresetBank
        @State var isSelected: Bool
        
        var body: some View
        {
            ZStack
            {
                VStack {
                    Text(bank.title)
                        .multilineTextAlignment(.center)
                        .fontWeight(.medium)
                        .foregroundColor(bank.isSelected == true ? .white : .black)
                        .font(.system(size: 12))
                        .minimumScaleFactor(0.5)
                        .lineLimit(2)
                        .padding(.top, 10)
                        .padding(.leading, 5)
                        .padding(.trailing, 5)
                    Spacer()
                    Text("(bank.presetsCount)")
                        .multilineTextAlignment(.center)
                        .font(.system(size: 10))
                        .foregroundColor(bank.isSelected == true ? .white : .black)
                        .padding(.bottom, 10)
                }
                ZStack {
                        Image(systemName: bank.iconString)
                            .font(.system(size: 26))
                            .foregroundColor(bank.isSelected == true ? .white : .black)
                   
                    
                }.padding(.top, 10)
            }
            .frame(width: 100, height: 100)
            .background {
                RoundedRectangle(cornerRadius: 8)
                   .aspectRatio(contentMode: .fill)
                   .foregroundColor(bank.isSelected == true ? .black : .white)
                   .shadow(color: .black.opacity(0.3), radius: 5, x: 0, y: 0)
                   .overlay(
                        RoundedRectangle(cornerRadius: 8)
                            .stroke(.gray, lineWidth: 2)
                    )
            }
            Spacer()
                .frame(width: 15)
        }
    }
    
    struct ContentView: View
    {
        @State var store: PresetStore
        var body: some View
        {
            ScrollView(.horizontal)
            {
                HStack {
                    
                    // Pull in and use the data passed in from UIKit.
                    
                    ForEach(store.arrayOfPresets) { bank in
                        PresetBankView(bank: bank, isSelected: bank.isSelected)
                    }
                }
                .padding()
            }
        }
    }
    

    Things update fine now from UIKit. I have 3 test buttons and cycle through the SwiftUI views isSelected property. Next to have the SwiftUI update itself and UIKit.


  2. You can replace the data struct on the hosting controller like this:

    @IBAction func testSelect(_ sender: UIButton) {
        hostingController.rootView = ContentView(bank:  thisStore[sender.tag])
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search