Unwrapping Optional in Swiftui View

Unwrapping optional in SwiftUI View

There are two ways to work with optionals in this context:

The first one, if you don't want this view to show at all if profile.height is nil:

profile.height.map({ TagBox(field: "height", value: String($0))})

The second one, if you want this view to show, but with a default value instead:

TagBox(field: "height", value: String(profile.height ?? 0))

How to unwrap an optional list of type Images in SwiftUI

The simplest direct answer to this is to use the same ?? operator you mentioned to provide an empty array:

struct ContentView : View {
@State var list_of_images : [Image]? = nil

var body: some View {
ForEach(0..<(list_of_images ?? []).count) {
list_of_images?[$0]
.resizable()
.aspectRatio(contentMode: .fill)
}
}
}

However, I'd be concerned in general about the idea of storing the Images themselves. I might look into another way of storing references to them (paths? names? UIImages?) that could be iterated over instead. In that case, you could do something like this:

struct ContentView : View {
@State var imageNames : [String]? = nil

var body: some View {
ForEach(imageNames ?? [], id: \.self) { imageName in
Image(imageName)
.resizable()
.aspectRatio(contentMode: .fill)
}
}
}

This approach doesn't work with the array of Images because Image doesn't conform to Identifiable

Update, based on comments:

struct ContentView : View {
@State var images : [UIImage]? = nil

var body: some View {
ForEach(images ?? [], id: \.self) { image in
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
}
}
}

SwiftUI unwrapping optionals inside View (pyramid of doom)

Several issues here:

  • generateNumbers and calculateAverageNumber depend on each other. So they need to await each other.

  • your "working code" does not match the description of your code. You say you want to show what ever finishes first but your if/else statements introduce dependencies between all 3 functions/views

  • you don´t need 3 different views. One that can be customized should be enough.



class NumberManager: ObservableObject {
@Published var integer: Int?
@Published var numbers: [Double]?
@Published var average: Double?


func generateInt() {
self.integer = Int.random(in: -10...10)
}

func generateNumbers() async {
self.numbers = (1...10_000_000).map { _ in Double.random(in: -10...10) }
// takes about 5 seconds to run...
}

// No need for arguments here
func calculateAverageNumber() async throws {
guard let numbers = numbers, !numbers.isEmpty else {
print("numbers not generated")
return
}
let total = numbers.reduce(0, +)
let average = total / Double(numbers.count)
self.average = average
}

//This function will handle the dependenies of generating the values and calculating the avarage
func calculateNumbersAndAvarage() async throws{
await generateNumbers()
try await calculateAverageNumber()
}
}

The View:

struct ContentView: View{

@StateObject private var numberManager = NumberManager()

var body: some View{
//Show the different detail views.
VStack{
Spacer()
Spacer()

DetailView(text: numberManager.integer != nil ? "Integer is \(numberManager.integer!)" : nil)
.onAppear {
numberManager.generateInt()
}
Spacer()
Group{
DetailView(text: isNumbersValid ? "First number is: \(numberManager.numbers![0])" : nil)

Spacer()

DetailView(text: numberManager.average != nil ? "Average is: \(numberManager.average!)" : nil)
}.onAppear {
Task{
do{
try await numberManager.calculateNumbersAndAvarage()
}
catch{
print("error")
}
}
}

Spacer()
Spacer()
}
}

//Just to make it more readable
var isNumbersValid: Bool{
numberManager.numbers != nil && numberManager.numbers?.count != 0
}
}

and the DetailView:

struct DetailView: View{

let text: String?

var body: some View{
// If no text to show, show `ProgressView`, or `LoadingView` in your case. You can inject the view directly or use a property for the String argument.
if let text = text {
Text(text)
.font(.headline)
.padding()
} else{
ProgressView()
}
}
}

The code should speak for itself. If you have any further question regarding this code please feel free to do so, but please read and try to understand how this works first.

Edit:

This does not wait for calculateAverageNumber to finish before displaying numbers[0]. The reason for it showing at the same time is that it takes almost no time to calculat the avarage. Try adding this between the 2 functions in calculateNumbersAndAvarage.

try await Task.sleep(nanoseconds: 4_000_000_000)

and you will see that it shows as it should.

How to unwrap an Optional Binding and pass value to another view?

You don't need boundModel for such body, use instead

var body: some View {
if selectedModel == nil {
Text("Kindly select a value in the list to start editing.")
} else {
EditModelView(model: Binding(selectedModel)!) // << here !!
}
}

SwiftUI - Unwrap optional image data to create Image based on UIImage(data)

You have a few options available. I think the most straightforward is to make an extension on Image and define a new initialiser that will serve your needs:

extension Image {

public init?(data: Data?) {
guard let data = data,
let uiImage = UIImage(data: data) else {
return nil
}
self = Image(uiImage: uiImage)
}
}

Then you can simply use it like this:

Image(data: userService.user.imageData)

since it is nullable it needs to be contained in another View with at least one other View.

If you want to provide a placeholder in place of a missing image you could use an extension like this:

extension Image {

public init(data: Data?, placeholder: String) {
guard let data = data,
let uiImage = UIImage(data: data) else {
self = Image(placeholder)
return
}
self = Image(uiImage: uiImage)
}
}

Then you can simply use it like this:

Image(data: userService.user.imageData, placeholder: "nameOfPlaceholder")

since it is not nullable it doe not need to be contained in another View with at least one other View.



Related Topics



Leave a reply



Submit