WWDC23 SALE: Save 50% on all my Swift books and bundles! >>

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      

Save 50% in my WWDC23 sale.

SAVE 50% To celebrate WWDC23, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.