NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

Hacking with macOS SwiftUI 2 - Buttons not refreshing

Forums > Books

Hi,

I bought Hacking with macOS from Apple Books as I wasn't aware that it doesn't come with the SwiftUI content, but when downloading the project files I got the swiftui folder as well. I wanted to at least look at the source code for the SwiftUI projects to see what it teaches, but when I opened and ran the 2nd SwiftUI project (Odd One Out) it didn't work for me. It seems like SwiftUI doesn't rebuild the UI when the layout array changes, resulting in a grid of empty rectangles. Is there a fix for this?

Xcode 11.5, macOS 10.15.5

Thanks! Jakub

1      

Hi Jakub - I hit the same issue. The problem seems to be with the if statement inside a for loop - for some reason that stops the view from refreshing. I got it to work by deleting the if self.image(row, column) == "empty" block and the if/else statement, leaving the loop as just:

ForEach(0..<Self.gridSize) { column in Button(action: { self.processAnswer(at: row, column) }) { Image(self.image(row, column)) .renderingMode(.original) } .buttonStyle(BorderlessButtonStyle()) }

It looks like if/else inside a ForEach loop stops the view from refreshing properly. I'm guessing this is a SwiftUI bug, but I don't know for sure. I could reproduce the problem with this stripped down example (which I mailed to Paul Hudson but no reply so far):

import SwiftUI

struct ContentView: View {
    @State var layout = "Initial” 

    var body: some View {
        ForEach(0..<1) { _ in
            if (true) {
                    Text(self.layout)
            }
        }
        .onAppear(perform: { self.layout = "Updated" })
        .frame(width: 400, height: 100)
    }
} 

Mark Russell

1      

Hey Mark,

Turns out this is actually a fully intentional performance optimization from the SwiftUI team. The ForEach(_:content:) initializer doesn't watch its data array for changes, so the view never rebuilds. The solution is to use the ForEach(_:id:content:) initializer, like so:

ForEach(0 ..< Self.gridSize, id: \.self) { row in
    HStack {
        ForEach(0 ..< Self.gridSize, id: \.self) { col in
            ...
        }
    }
}

--Jakub

1      

The project runs perfectly with xcode 12 - I've just tried it with BigSur beta 8.

1      

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.