Reputation: 419
I am working with an image carousel that uses bullets for pagination. The total width of the carousel and the number of bullets depends on the number of images.
Here are my vars
struct UserView: View {
@State var user_id : String = ""
@State var profilePhotos : [Photo] = []
@State var pages = 1
}
Here is my onAppear
function
GetProfilePhotos().browseUserPhotos(user_id: self.user_id){(photos) in
self.profilePhotos = photos
self.pages = photos.count
}
Downstream, I use these variables to build the carousel.
struct Carousel : UIViewRepresentable {
// ....
var width : CGFloat
@Binding var page : Int
@State var user_id : String
@Binding var pages : Int
@Binding var profilePhotos : [Photo]
var height : CGFloat
func makeUIView(context: Context) -> UIScrollView{
let total = width * CGFloat(self.pages)
let view = UIScrollView()
view.isPagingEnabled = true
view.contentSize = CGSize(width: total, height: 1.0)
view.bounces = true
view.showsVerticalScrollIndicator = false
view.showsHorizontalScrollIndicator = false
view.delegate = context.coordinator
let view1 = UIHostingController(rootView: ListCards(page: self.$page, profilePhotos: self.$profilePhotos))
view1.view.frame = CGRect(x: 0, y: 0, width: total, height: self.height)
view1.view.backgroundColor = .clear
view.addSubview(view1.view)
return view
}
// ...
}
self.profilePhotos
works JUST FINE. I can read out my images with no problem. self.pages
however is always set to 1
. It never changes even though I am using the same process to update both variables.
Can anyone explain what I'm missing?
Below is my ListCard
struct ListCards: View {
@Binding var page : Int
@Binding var profilePhotos : [Photo]
var body: some View{
HStack(spacing:0){
ForEach(self.profilePhotos){photo in
Card(page: self.$page, width: UIScreen.main.bounds.width, data: photo)
}
}.onAppear(){
print(self.profilePhotos)
}
}
}
Upvotes: 1
Views: 78
Reputation: 257693
In representable you have to move bindings-dependent code into updateUIView
, `cause it is a method called whenever some of binding changed.
Here is a correct way (not tested due to absent dependent entities, so you might need to tune it)
struct Carousel : UIViewRepresentable {
// ...
var width : CGFloat
@Binding var page : Int
@State var user_id : String
@Binding var pages : Int
@Binding var profilePhotos : [Photo]
var height : CGFloat
func makeUIView(context: Context) -> UIScrollView{
let view = UIScrollView()
view.isPagingEnabled = true
view.bounces = true
view.showsVerticalScrollIndicator = false
view.showsHorizontalScrollIndicator = false
view.delegate = context.coordinator
return view
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
uiView.subviews.last?.removeFromSuperview()
let total = width * CGFloat(self.pages)
uiView.contentSize = CGSize(width: total, height: 1.0)
let view1 = UIHostingController(rootView: ListCards(page: self.$page, profilePhotos: self.$profilePhotos))
view1.view.frame = CGRect(x: 0, y: 0, width: total, height: self.height)
view1.view.backgroundColor = .clear
uiView.addSubview(view1.view)
}
}
also, any API callback might be executed on unknown background queue, but states are better to update on UI queue, so
GetProfilePhotos().browseUserPhotos(user_id: self.user_id){(photos) in
DispatchQueue.main.async { // redirect to main queue
self.profilePhotos = photos
self.pages = photos.count
}
}
Upvotes: 1