Reputation: 95
I'm a beginning programmer so...
Using Xcode 12.4 on iMac Big Sur 11.2 the project is 'Multiplatform App'
I'm trying to build an array of images to be moved around in a grid. I copied "My Image.png" into my Xcode project and used it just fine but no matter how I try to put it in an array it returns a nil array. I tried this on another computer and it seemed to work. What in Xcode could affect this? (Or did I just imagine it worked on the other machine? :-p)
code:
@State var stringURL: URL = Bundle.main.url(forResource: "My Image", withExtension: "png")!
Image(nsImage: NSImage(byReferencing: stringURL))
@State var arrayURLs = [URL]()
arrayURLs.append(Bundle.main.url(forResource: "My Image", withExtension: "png")!)
Image(nsImage: NSImage(byReferencing: arrayURLs[0]))
first 2 lines work but the last 3 lines lines fail in the same app. I get runtime error 'Index out of Range' which I believe is because the bundle call returned nil.
In context is basically the same. I am at the very start of this project and never got past the first lines....
import SwiftUI
struct ContentView: View {
@State var pieceImageURLs = [URL]()
init() {
self.pieceImageURLs.append(Bundle.main.url(forResource: "Red Warrior", withExtension: "png")!)
}
var body: some View {
HStack {
Image(nsImage: NSImage(byReferencing: pieceImageURLs[0]))
}
}}
import SwiftUI
struct ContentView: View {
@State var stringURL: URL = Bundle.main.url(forResource: "My Image", withExtension: "png")!
@State var arrayURLs = [URL]()
init() {
stringURL = Bundle.main.url(forResource: "My Image", withExtension: "png")!
arrayURLs.append(Bundle.main.url(forResource: "My Image", withExtension: "png")!)
}
var body: some View {
HStack {
Image(nsImage: NSImage(byReferencing: stringURL))
Image(nsImage: NSImage(byReferencing: arrayURLs[0]))
}
Button(action: {
arrayURLs.append(Bundle.main.url(forResource: "My Image", withExtension: "png")!)
Image(nsImage: NSImage(byReferencing: arrayURLs[0]))
}) {
Text("add image")
}
}
}
'''
Or all together like this either image line with arrays fails
Upvotes: 7
Views: 4303
Reputation: 52645
This is an interesting one and I wouldn't have predicted the behavior myself. Looks like SwiftUI really doesn't like you to set @State variables during the init phase in a View. There are a couple ways around that.
You can't set a new value, but you can initialize it explicitly as State
in your init:
struct ContentView: View {
@State var pieceImageURLs = [URL]()
init() {
if let url = Bundle.main.url(forResource: "Frame 1-2", withExtension: "png") {
_pieceImageURLs = State(initialValue: [url]) // <--- Here
}
}
var body: some View {
HStack {
if let imgUrl = pieceImageURLs.first, let nsImage = NSImage(byReferencing: imgUrl) {
Image(nsImage: nsImage)
} else {
Text("HI")
}
}
.frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
}
}
Don't use @State for this, since it's static anyway (at least in your example) -- you're just setting a value in init.
struct ContentView: View {
var pieceImageURLs = [URL]() // <--- Here
init() {
if let url = Bundle.main.url(forResource: "Frame 1-2", withExtension: "png") {
pieceImageURLs.append(url)
}
}
var body: some View {
HStack {
if let imgUrl = pieceImageURLs.first, let nsImage = NSImage(byReferencing: imgUrl) {
Image(nsImage: nsImage)
} else {
Text("HI")
}
}
.frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
}
}
Use state, but set it in onAppear
:
struct ContentView: View {
@State var pieceImageURLs = [URL]()
var body: some View {
HStack {
if let imgUrl = pieceImageURLs.first, let nsImage = NSImage(byReferencing: imgUrl) {
Image(nsImage: nsImage)
} else {
Text("HI")
}
}
.onAppear { // <--- Here
if let url = Bundle.main.url(forResource: "Frame 1-2", withExtension: "png") {
pieceImageURLs.append(url)
}
}
.frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
}
}
Do the init in a view model (ObservableObject
)
class ViewModel : ObservableObject { // <--- Here
@Published var pieceImageURLs = [URL]()
init() {
if let url = Bundle.main.url(forResource: "Frame 1-2", withExtension: "png") {
pieceImageURLs.append(url)
}
}
}
struct ContentView: View {
@ObservedObject var viewModel = ViewModel() // <--- Here
var body: some View {
HStack {
if let imgUrl = viewModel.pieceImageURLs.first, let nsImage = NSImage(byReferencing: imgUrl) {
Image(nsImage: nsImage)
} else {
Text("HI")
}
}
.frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
}
}
Upvotes: 7