1

I have some text inside a View and I want this text to change it's position on timer. I have the following:

struct AlphabetView: View {
    @State var timer: Publishers.Autoconnect<Timer.TimerPublisher> = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
    @State var refreshMe: Bool = false // this is a hack to refresh the view
    
    var relativePositionX: CGFloat {
        get { return CGFloat(Float.random(in: 0...1)) }
    }
    
    var relativePositionY: CGFloat {
        get { return CGFloat(Float.random(in: 0...1)) }
    }
    
    var body: some View {
        GeometryReader { geometry in
            Text("Hello World!")
                .position(x: geometry.size.width * self.relativePositionX, y: geometry.size.height * self.relativePositionY)
                .onReceive(timer) { _ in
                    // a hack to refresh the view
                    self.refreshMe = !self.refreshMe
                }
        }
    }
}

I suppose that the View should be reloaded every time self.refreshMe changes, because it is @State. But what I see is that the text position changes only when the parent view of the view in question is reloaded. What am I doing wrong (except hacking around)? Is there a better way to do this?

2
  • 1
    Your hack isn't working because your view doesn't actually use refreshMe. Adding something like .foregroundColor(self.refreshMe ? .black : .black) is enough to get SwiftUI to trigger the update. Commented Oct 11, 2020 at 18:47
  • 1
    BTW, CGFloat also has .random(in:), so var relativePositionX: CGFloat { .random(in: 0...1) } is sufficient. Commented Oct 11, 2020 at 19:44

1 Answer 1

2

Instead of hack just update position directly.

Here is corrected variant. Tested with Xcode 12 / iOS 14.

struct AlphabetView: View {
    let timer: Publishers.Autoconnect<Timer.TimerPublisher> = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()

    @State var point: CGPoint = .zero
    
    var relativePositionX: CGFloat {
        get { return CGFloat(Float.random(in: 0...1)) }
    }
    
    var relativePositionY: CGFloat {
        get { return CGFloat(Float.random(in: 0...1)) }
    }
    
    var body: some View {
        GeometryReader { geometry in
            Text("Hello World!")
                .position(self.point)
                .onReceive(timer) { _ in
                    self.point = CGPoint(x: geometry.size.width * self.relativePositionX, y: geometry.size.height * self.relativePositionY)
                }
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This solves the problem completely, thank you very much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.