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
I think this solves things. Here is my updated code:
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.You can replace the data struct on the hosting controller like this: