UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Help making this code "better"?

Forums > SwiftUI

I have this view below that basically slides in a Rectangle with color after 3.5 seconds.

But I'd like to refactor the code to allow for more slides and not have to do an if-else for each.

What would be the best way to go around doing that? How would you guys "clean up" this code?

I'd start with creating an inner Rectangle view to contain the similar codes but what do I do with the show1/2/3 code?

thanks in advance, Jayson

struct SlideView: View {

    @State private var show1 = true
    @State private var show2 = false
    @State private var show3 = false

    @State private var hasTimeElapsed = false

    private func delay() async {
        try? await Task.sleep(nanoseconds: 3_500_000_000)
        hasTimeElapsed = true
    }

    var body: some View {

        if show1 == true {
            Rectangle()
                .fill(Color.blue)
                .ignoresSafeArea()
                .transition(.slide)
                .task {
                    await delay()
                    if hasTimeElapsed == true {
                        withAnimation {
                            show1.toggle()
                            show2.toggle()
                        }
                    }
                }
        } else if show2 == true {
            Rectangle()
                .fill(Color.red)
                .ignoresSafeArea()
                .transition(.slide)
                .task {
                    await delay()
                    if hasTimeElapsed == true {
                        withAnimation {
                            show2.toggle()
                            show3.toggle()
                        }
                    }
                }

        } else if show3 == true {
            Rectangle()
                .fill(Color.yellow)
                .ignoresSafeArea()
                .transition(.slide)
                .task {
                    await delay()
                    if hasTimeElapsed == true {
                        withAnimation {
                            show3.toggle()
                            show1.toggle()
                        }
                    }
                }
            }
    }

}

2      

You could use a enum.

enum Views {
  case one
  case two
  case three
}

@State private var show:Views = .one

Then use a switch statement and switch on show. In your task set show to the enum case you want to show.

3      

thanks @hatsushira. I don't know why I didn't think of that earlier. :)

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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

struct SlideView: View {

    @State private var show: ColorView = .blue

    // @State private var index: Int = 0

    //  let viewArray: [ColorView]
    var nanoseconds: UInt64

    enum ColorView: CaseIterable {
        case blue
        case yellow
        case red
    }

    private func delay() async {
        try? await Task.sleep(nanoseconds: nanoseconds)
    }

    private struct RectangleView: View {
        var color: Color

        init(color: Color) {
            self.color = color
        }

        var body: some View {
            Rectangle()
                .fill(color)
                .ignoresSafeArea()
                .transition(.slide)
        }
    }

    init(delayInNanoseconds: UInt64 = 2_000_000_000) {
        // viewArray      = ColorView.allCases
        nanoseconds    = delayInNanoseconds
    }

    // MARK: - --------------  Body  ---------------
    var body: some View {

        switch show {
        case .blue:
            RectangleView(color: .blue)
                .task {
                    await delay()
                    withAnimation {
                        show = .yellow
                    }
                }

        case .yellow:
            RectangleView(color: .yellow)
                .task {
                    await delay()
                    withAnimation {
                        show = .red
                    }
                }
        case .red:
            RectangleView(color: .red)
                .task {
                    await delay()
                    withAnimation {
                        show = .blue
                    }
                }
        }
    }
}

Updated code implementing switch.

I tried using an array approach hoping to just go through each array item and cycling through it.

I have a viewArray property I can use but for the life of me, I can't figure out how to have it cycle through each array item and then loop back to the start with .transition(.slide)

Can anyone point me to the right direction? I am hoping to have the code work with any number of colors I place in the Enum / array. As of now, I'd have to manually place each case in the switch body.

thanks,

2      

Try this:

struct SlideView: View {

    @State private var currentColor: Color = .blue
    private let colorArray: [Color] = [.blue, .yellow, .red]

    private var nanoseconds: UInt64

    private var nextColor: Color {
        let currentIdx = colorArray.firstIndex(of: currentColor)!
        let nextIdx = colorArray.index(after: currentIdx)
        return colorArray[nextIdx == colorArray.endIndex ? colorArray.startIndex : nextIdx]
    }

    private func delay() async {
        try? await Task.sleep(nanoseconds: nanoseconds)
    }

    init(delayInNanoseconds: UInt64 = 2_000_000_000) {
        nanoseconds = delayInNanoseconds
    }

    // MARK: - --------------  Body  ---------------
    var body: some View {
        Rectangle()
            .fill(currentColor)
            .ignoresSafeArea()
            .transition(.slide)
            .task {
                await delay()
                withAnimation {
                    currentColor = nextColor
                }
            }
            .id(currentColor)
    }
}

3      

@roosterboy that's perfect. works like a charm.

I didn't even think of using a computed property to get the nextIndex.

Follow up questions - what's the .id() for and why does it only work with it placed after .task { } ?

2      

From Apple's documentation

When the proxy value specified by the id parameter changes, the identity of the view — for example, its state — is reset.

And when the state of the view is reset the view is redrawn and as a result the new color is visible.

Some SwiftUI modifiers require a certain order. As I understand is that .id is always the last. At least I was never able to place it not at the last spot.

3      

that makes sense. thank you both.

2      

Since the .task() modifier is only fired when a View first appears, we need to ensure that each change of the fill color results in a new View rather than just modifying the existing View or it will only ever fire one time. Thus we assign a new id each time the color changes so that a new task is run.

Instead of using a separate .id() modifier, we could have used this:

.task(id: currentColor) {
    await delay()
    currentColor = nextColor
}

This version of .task() creates a new task every time the value passed into the id parameter changes. I actually think that's a bit cleaner and nicer to read.

2      

I love it!

Implementing that now.

edit: seems I lose the swipe transition if I do task(id:) instead of .id().

2      

seems I lose the swipe transition if I do task(id:) instead of .id().

Ah, didn't even notice that. Sorry!

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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.