SwiftUI + Timer + AVPlayer - When audio playing, onReceive(timer) not triggered - swift

I am having issue to make my timer (and my animation) to work properly. My timer is not triggered if AVPlayer is currently playing. As soon as I pause it, my timer is resuming...
I have an implementation of a SlidingText view. Basically just moving a text in its box from left to right and then right to left. This animation is triggered with a Timer every 5 seconds.
I have an AVPlayer playing something at the same time with a pause/play button.
This is the implementation of the SlidingText. And the AVPlayer is just being called with a button that triggers player.play() or player.pause().
import SwiftUI
struct SlidingText: View {
let geometryProxy: GeometryProxy
#Binding var text: String
let font: Font
#State private var animateSliding: Bool = false
private let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect()
private let slideDuration: Double = 3
var body: some View {
ZStack(alignment: .leading, content: {
VStack(content: {
Text(text)
.font(self.font)
.foregroundColor(.white)
.background(Color.red)
})
.id("SlidingText-Animation")
.fixedSize(horizontal: false, vertical: true)
.frame(width: geometryProxy.size.width, alignment: animateSliding ? .trailing : .leading)
.clipped()
.animation(Animation.linear(duration: slideDuration))
.onReceive(timer, perform: { _ in
self.animateSliding.toggle()
})
})
.frame(width: self.geometryProxy.size.width, height: self.geometryProxy.size.height)
.background(Color.yellow)
}
}
Is there anything wrong it the way I use the timer here?
Thanks for any future help!

Alright, I endup not using a timer because it is buggy.
for those interested it looks like this now, I am using the Animation properties.
No more interferences with the audio player.
struct SlidingText: View {
let geometryProxy: GeometryProxy
#Binding var text: String
let font: Font
#State private var animateSliding: Bool = false
private let slideDelay: Double = 3
private let slideDuration: Double = 6
private var isTextLargerThanView: Bool {
if text.size(forWidth: geometryProxy.size.width, andFont: font).width < geometryProxy.size.width {
return false
}
return true
}
var body: some View {
ZStack(alignment: .leading, content: {
VStack(content: {
Text(text)
.font(self.font)
.foregroundColor(.white)
.background(Color.red)
})
.id("SlidingText-Animation")
.fixedSize()
.animation(Animation.linear(duration: slideDuration).delay(slideDelay).repeatForever(autoreverses: true))
.frame(width: geometryProxy.size.width,
alignment: isTextLargerThanView ? (animateSliding ? .trailing : .leading) : .center)
.onAppear(perform: {
self.animateSliding.toggle()
})
})
.clipped()
}
}

Related

I'm trying to create an animation for prompts in a search bar like in Instagram

This is what my code creates
I want the prompts to keep moving up one after the other in a continuous cycle like on Instagram but am not able to move the text up a second time and it comes back down. Is it possible to have two offset modifiers for the same text so that after moving up into the bar I can move it up a second time so it moves out of the view?
#State private var list: [String] = ["Search \"puppy biting\"","Search \"deworming\""]
#State private var currentPrompt:Int = 0
#State private var timer: Timer?
#State private var playAnimation : Bool = false
#State private var playAnimation2 : Bool = false
ZStack {
VStack {
HStack {
if !list.isEmpty {
HStack {
ZStack {
Text(list[0])
.foregroundColor(.gray)
.font(.fontFor(.regular, size:
.frame(alignment: .leading)
.offset(y: playAnimation ? 30:0)
.opacity(playAnimation ? 0:1)
Text(list[1])
.foregroundColor(.gray)
.font(.fontFor(.regular, size:
.frame(alignment: .leading)
.offset(y: playAnimation2 ? 30:0)
.opacity(playAnimation ? 1:0)
}
Spacer()
}
.frame(height: 20, alignment: .leading)
Spacer()
}
}.padding(.horizontal)
.padding(.vertical,10)
.background(Color("searchBg"))
.cornerRadius(30)
}
.padding(.horizontal, 20)
.padding(.bottom, 20)
.background(Color.white.ignoresSafeArea(edges: .top))
}
.onAppear {
timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { timer in
withAnimation(.easeInOut.delay(1)) {
playAnimation.toggle()
if playAnimation {
playAnimation2.toggle()
}
if playAnimation2 {
playAnimation3.toggle()
}
}
}
}

Alarm Button interacts incorrectly with view - SwiftUI

I'm continuing to develop a timer app for practice purposes and have the basic functions ready so far.
The timer works in such a way that if you click on "Start" the timer simply runs down to 0 and then selects the next player - here you can also the button in the upper middle whether an alarm is played after the timer or not - however, this also stops the timer, although this does not occur in the implementation (see also video below). I hope someone can help me.
For the timer I made a StopWatchManager class:
import Foundation
class StopWatchManager : ObservableObject {
#Published var secondsElapsed : Double = 0.00
#Published var mode : stopWatchMode = .stopped
var timer : Timer = Timer()
//Start the timer
func start() -> Void {
secondsElapsed = 0.00
mode = .running
timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true){ _ in
self.secondsElapsed += 0.01
}
}
//Pause the timer
func pause() -> Void {
timer.invalidate()
mode = .paused
}
//Stop the timer
func stop() -> Void {
timer.invalidate()
secondsElapsed = 0.00
self.mode = .stopped
}
}
enum stopWatchMode{
case running
case stopped
case paused
}
Then I have an overview TimerView which implements some little details and connects the buttons with TimerTextView:
import SwiftUI
struct TimerView: View {
#State private var alarmSoundOn : Bool = true
#State private var quitGame : Bool = false
//buttons variable for timer
#State private var startTimer : Bool = false
#State private var resetTimer : Bool = false
#State private var pauseTimer : Bool = false
#State private var quitTimer : Bool = false
var body: some View {
ZStack{
Color.blue.ignoresSafeArea()
TimerTextView(startTimer: $startTimer, resetTimer: $resetTimer, pauseTimer: $pauseTimer, quitTimer: $quitTimer, playAlarmSound: $alarmSoundOn)
if (!quitGame) {
VStack{
HStack(alignment: .top){
//TODO - find bug with Timer
Button(action: {
alarmSoundOn.toggle()
}, label: {
if (alarmSoundOn) {
Image(systemName: "speaker.circle")
.resizable().frame(width: 30, height: 30)
} else {
Image(systemName: "speaker.slash.circle")
.resizable().frame(width: 30, height: 30)
}
}).foregroundColor(.white)
}
Spacer()
}
}
}
}
}
And here is my TimerTextView where all the logic with the buttons and the circle happens:
import SwiftUI
import AVFoundation
struct TimerTextView: View {
//timer instance
#ObservedObject var playTimer : StopWatchManager = StopWatchManager()
//default buttons for the timer
#Binding var startTimer : Bool
#Binding var resetTimer : Bool
#Binding var pauseTimer : Bool
#Binding var quitTimer : Bool
//variables for circle
var timerSeconds : Int = 10
#State private var progress : Double = 1.00
#State private var endDate : Date? = nil
//seconds of timer as text in the middle of the timer
#State var textTimerSeconds : Double = 10
//sound for the alarm after the timer exceeded
#Binding var playAlarmSound : Bool
#State private var playAlarmAtTheEnd : Bool = true
let sound : SystemSoundID = 1304
var body: some View {
ZStack{
if (quitTimer) {
EmptyView()
} else {
//Circles---------------------------------------------------
VStack{
VStack{
VStack{
ZStack{
//Circle
ZStack{
Circle()
.stroke(lineWidth: 20)
.foregroundColor(.white.opacity(0.3))
Circle()
.trim(from: 0.0, to: CGFloat(Double(min(progress, 1.0))))
.stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(270.0))
.foregroundColor(.white)
.animation(.linear, value: progress)
}.padding()
VStack{
//Timer in the middle
Text("\(Int(textTimerSeconds.rounded(.up)))")
.font(.system(size: 80))
.bold()
.multilineTextAlignment(.center)
.foregroundColor(.white)
}
}.padding()
//timer responds every milliseconds and calls intern function decrease timer
.onReceive(playTimer.$secondsElapsed, perform: { _ in
decreaseTimer()
})
//pauses the timer
.onChange(of: pauseTimer, perform: { change in
if (pauseTimer) {
playTimer.pause()
} else {
endDate = Date(timeIntervalSinceNow: TimeInterval(textTimerSeconds))
playTimer.start()
}
})
//resets the timer
.onChange(of: resetTimer, perform: { change in
if (resetTimer) {
resetTimerToBegin()
resetTimer = false
}
})
//play alarm sound at the end of the timer
.onChange(of: playAlarmSound, perform: { change in
if (playAlarmSound){
playAlarmAtTheEnd = true
} else {
playAlarmAtTheEnd = false
}
})
.onChange(of: startTimer, perform: { change in startTimerFromBegin()
})
}
}.foregroundColor(Color.black)
//----------------- Buttons
VStack(spacing: 50){
//if isStoped -> show play, reset & quit
//if !isStoped -> show pause
if(pauseTimer){
HStack(alignment: .bottom){
Spacer()
Spacer()
//Play Button
Button(action: {
pauseTimer = false
}, label: {
VStack(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/, spacing: 5){
Image(systemName: "play.fill")
Text("Play").font(.callout)
}
})
.font(.title2)
.frame(width: 60, height: 60, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.padding(3.0)
.buttonStyle(BorderlessButtonStyle())
Spacer()
//Reset Button
Button(action: {
resetTimer = true
}, label: {
VStack(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/, spacing: 5){
Image(systemName: "gobackward")
Text("Reset").font(.callout)
}
})
.font(.title2)
.frame(width: 60, height: 60, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.padding(3.0)
.buttonStyle(BorderlessButtonStyle())
Spacer()
//Quit Button
Button(action: {
quitTimer = true
}, label: {
VStack(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/, spacing: 5){
Image(systemName: "flag.fill")
Text("Exit").font(.callout)
}
})
.font(.title2)
.frame(width: 60, height: 60, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.padding(3.0)
.buttonStyle(BorderlessButtonStyle())
Spacer()
Spacer()
}
} else if (startTimer) {
//Pause Button
Button(action: {
pauseTimer = true
}, label: {
VStack(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/, spacing: 5){
Image(systemName: "pause.fill")
Text("Pause").font(.callout)
}
})
.font(.title2)
.frame(width: 60, height: 60, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.padding(3.0)
.buttonStyle(BorderlessButtonStyle())
} else {
//Play Button
Button(action: {
pauseTimer = false
startTimer = true
}, label: {
VStack(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/, spacing: 5){
Image(systemName: "play.fill")
Text("Start").font(.callout)
}
})
.font(.title2)
.frame(width: 60, height: 60, alignment: .center)
.padding(3.0)
.buttonStyle(BorderlessButtonStyle())
}
}.foregroundColor(.white)
}
}
}
}
//FUNCTIONS --------------------------------
private func startTimerFromBegin() -> Void{
endDate = Date(timeIntervalSinceNow: TimeInterval(timerSeconds))
playTimer.start()
}
private func resetTimerToBegin() -> Void {
endDate = Date(timeIntervalSinceNow: TimeInterval(timerSeconds))
progress = 1.0
textTimerSeconds = Double(timerSeconds)
}
private func decreaseTimer() -> Void{
guard let endDate = endDate else { print("decreaseTimer() was returned"); return }
progress = max(0, endDate.timeIntervalSinceNow / TimeInterval(timerSeconds))
textTimerSeconds -= 0.01
if endDate.timeIntervalSinceNow <= 0 {
if (playAlarmAtTheEnd) {
AudioServicesPlayAlertSound(sound)
}
}
}
}
You are instantiating your playTimer in the subview ... That will reset it when the view is redrawn. Instead you should do this in the parent view and pass it down.
Also you should use #StateObject to instantiate.
struct TimerView: View {
//timer instance HERE
#StateObject var playTimer : StopWatchManager = StopWatchManager()
...
pass down to subview
// pass timer here
TimerTextView(playTimer: playTimer, startTimer: $startTimer, resetTimer: $resetTimer, pauseTimer: $pauseTimer, quitTimer: $quitTimer, playAlarmSound: $alarmSoundOn)
Subview:
struct TimerTextView: View {
//passed timer
#ObservedObject var playTimer : StopWatchManager
...
One more thing: Firing the timer every 0.01 seconds is too much. You should go to 0.1

SwiftUI Custom View repeat forever animation show as unexpected

I created a custom LoadingView as a Indicator for loading objects from internet. When add it to NavigationView, it shows like this
enter image description here
I only want it showing in the middle of screen rather than move from top left corner
Here is my Code
struct LoadingView: View {
#State private var isLoading = false
var body: some View {
Circle()
.trim(from: 0, to: 0.8)
.stroke(Color.primaryDota, lineWidth: 5)
.frame(width: 30, height: 30)
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
.onAppear {
withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
self.isLoading.toggle()
}
}
}
}
and my content view
struct ContentView: View {
var body: some View {
NavigationView {
LoadingView()
.frame(width: 30, height: 30)
}
}
}
This looks like a bug of NavigationView: without it animation works totally fine. And it wan't fixed in iOS15.
Working solution is waiting one layout cycle using DispatchQueue.main.async before string animation:
struct LoadingView: View {
#State private var isLoading = false
var body: some View {
Circle()
.trim(from: 0, to: 0.8)
.stroke(Color.red, lineWidth: 5)
.frame(width: 30, height: 30)
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
.onAppear {
DispatchQueue.main.async {
withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
self.isLoading.toggle()
}
}
}
}
}
This is a bug from NavigationView, I tried to kill all possible animation but NavigationView ignored all my try, NavigationView add an internal animation to children! here all we can do right now!
struct ContentView: View {
var body: some View {
NavigationView {
LoadingView()
}
}
}
struct LoadingView: View {
#State private var isLoading: Bool = Bool()
var body: some View {
Circle()
.trim(from: 0, to: 0.8)
.stroke(Color.blue, lineWidth: 5.0)
.frame(width: 30, height: 30)
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
.animation(Animation.linear(duration: 1).repeatForever(autoreverses: false), value: isLoading)
.onAppear { DispatchQueue.main.async { isLoading.toggle() } }
}
}

SwiftUI - Reload/Force the view to update/stop the animation

I have a view that make the text slide from left to right when the text is too long for its context.
import SwiftUI
struct SlidingText: View {
let geometryProxy: GeometryProxy
#Binding var text: String
let font: Font
#State private var animateSliding: Bool = false
private let slideDelay: Double = 3
private let slideDuration: Double = 6
private var isTextLargerThanView: Bool {
if text.size(forWidth: geometryProxy.size.width, andFont: font).width < geometryProxy.size.width {
return false
}
return true
}
var body: some View {
ZStack(alignment: .leading, content: {
VStack(content: {
Text(text)
.font(self.font)
.foregroundColor(.white)
})
.id("SlidingText-Animation")
.fixedSize()
.animation(isTextLargerThanView ? Animation.linear(duration: slideDuration).delay(slideDelay).repeatForever(autoreverses: true) : nil)
.frame(width: geometryProxy.size.width,
alignment: isTextLargerThanView ? (animateSliding ? .trailing : .leading) : .center)
.onAppear(perform: {
self.animateSliding.toggle()
})
})
.clipped()
}
}
The problem here is, if I have a text that was too long, it will do the correct animation, but then when I change the text during the animation, it doesn't take in consideration the new size and the isTextLargerThanView is ignored. I also tried a didSet for my #Binding var text: String, it doesn't work either.
I am not really sure how to handle this case here, I tried to use custom Binders but didn't work. I know in iOS 14 there will be a onUpdate(on: _) function but I need it to work on iOS 13 too.
Do you have an idea of what I could do to update my text and be able to update the isTextLargerThanView?
Thank you for any future help!
I can't run your code. Maybe you can try to make it reactive like this:
#Binding var text: String {
didSet {
isTextLargerThanView = (text.size(forWidth: geometryProxy.size.width, andFont: font).width > geometryProxy.size.width)
}
}
...
#State private var isTextLargerThanView = false

SwiftUI - Sliding Text animation and positioning

On my journey to learn more about SwiftUI, I am still getting confused with positioning my element in a ZStack.
The goal is "simple", I wanna have a text that will slide within a defined area if the text is too long.
Let's say I have an area of 50px and the text takes 100. I want the text to slide from right to left and then left to right.
Currently, my code looks like the following:
struct ContentView: View {
#State private var animateSliding: Bool = false
private let timer = Timer.publish(every: 1, on: .current, in: .common).autoconnect()
private let slideDuration: Double = 3
private let text: String = "Hello, World! My name is Oleg and I would like this text to slide!"
var body: some View {
GeometryReader(content: { geometry in
VStack(content: {
Text("Hello, World! My name is Oleg!")
.font(.system(size: 20))
.id("SlidingText-Animation")
.alignmentGuide(VerticalAlignment.center, computeValue: { $0[VerticalAlignment.center] })
.position(y: geometry.size.height / 2 + self.textSize(fromWidth: geometry.size.width).height / 2)
.fixedSize()
.background(Color.red)
.animation(Animation.easeInOut(duration: 2).repeatForever())
.position(x: self.animateSliding ? -self.textSize(fromWidth: geometry.size.width).width : self.textSize(fromWidth: geometry.size.width).width)
.onAppear(perform: {
self.animateSliding.toggle()
})
})
.background(Color.yellow)
.clipShape(Rectangle())
})
.frame(width: 200, height: 80)
}
func textSize(fromWidth width: CGFloat, fontName: String = "System Font", fontSize: CGFloat = UIFont.systemFontSize) -> CGSize {
let text: UILabel = .init()
text.text = self.text
text.numberOfLines = 0
text.font = UIFont.systemFont(ofSize: 20) // (name: fontName, size: fontSize)
text.lineBreakMode = .byWordWrapping
return text.sizeThatFits(CGSize.init(width: width, height: .infinity))
}
}
Do you have any suggestion how to center the Text in Vertically in its parent and do the animation that starts at the good position?
Thank you for any future help, really appreciated!
EDIT:
I restructured my code, and change a couple things I was doing.
struct SlidingText: View {
let geometryProxy: GeometryProxy
#State private var animateSliding: Bool = false
private let timer = Timer.publish(every: 1, on: .current, in: .common).autoconnect()
private let slideDuration: Double = 3
private let text: String = "Hello, World! My name is Oleg and I would like this text to slide!"
var body: some View {
ZStack(alignment: .leading, content: {
Text(text)
.font(.system(size: 20))
// .lineLimit(1)
.id("SlidingText-Animation")
.fixedSize(horizontal: true, vertical: false)
.background(Color.red)
.animation(Animation.easeInOut(duration: slideDuration).repeatForever())
.offset(x: self.animateSliding ? -textSize().width : textSize().width)
.onAppear(perform: {
self.animateSliding.toggle()
})
})
.frame(width: self.geometryProxy.size.width, height: self.geometryProxy.size.height)
.clipShape(Rectangle())
.background(Color.yellow)
}
func textSize(fontName: String = "System Font", fontSize: CGFloat = 20) -> CGSize {
let text: UILabel = .init()
text.text = self.text
text.numberOfLines = 0
text.font = UIFont(name: fontName, size: fontSize)
text.lineBreakMode = .byWordWrapping
let textSize = text.sizeThatFits(CGSize(width: self.geometryProxy.size.width, height: .infinity))
print(textSize.width)
return textSize
}
}
struct ContentView: View {
var body: some View {
GeometryReader(content: { geometry in
SlidingText(geometryProxy: geometry)
})
.frame(width: 200, height: 40)
}
}
Now the animation looks pretty good, except that I have padding on both right and left which I don't understand why.
Edit2: I also notice by changing the text.font = UIFont.systemFont(ofSize: 20) by text.font = UIFont.systemFont(ofSize: 15) makes the text fits correctly. I don't understand if there is a difference between the system font from SwiftUI or UIKit. It shouldn't ..
Final EDIT with Solution in my case:
struct SlidingText: View {
let geometryProxy: GeometryProxy
#Binding var text: String
#Binding var fontSize: CGFloat
#State private var animateSliding: Bool = false
private let timer = Timer.publish(every: 5, on: .current, in: .common).autoconnect()
private let slideDuration: Double = 3
var body: some View {
ZStack(alignment: .leading, content: {
VStack {
Text(text)
.font(.system(size: self.fontSize))
.background(Color.red)
}
.fixedSize()
.frame(width: geometryProxy.size.width, alignment: animateSliding ? .trailing : .leading)
.clipped()
.animation(Animation.linear(duration: slideDuration))
.onReceive(timer) { _ in
self.animateSliding.toggle()
}
})
.frame(width: self.geometryProxy.size.width, height: self.geometryProxy.size.height)
.clipShape(Rectangle())
.background(Color.yellow)
}
}
struct ContentView: View {
#State var text: String = "Hello, World! My name is Oleg and I would like this text to slide!"
#State var fontSize: CGFloat = 20
var body: some View {
GeometryReader(content: { geometry in
SlidingText(geometryProxy: geometry, text: self.$text, fontSize: self.$fontSize)
})
.frame(width: 400, height: 40)
.padding(0)
}
}
Here's the result visually.
Here is a possible simple approach - the idea is just as simple as to change text alignment in container, anything else can be tuned as usual.
Demo prepared & tested with Xcode 12 / iOS 14
Update: retested with Xcode 13.3 / iOS 15.4
struct DemoSlideText: View {
let text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor"
#State private var go = false
var body: some View {
VStack {
Text(text)
}
.fixedSize()
.frame(width: 300, alignment: go ? .trailing : .leading)
.clipped()
.onAppear { self.go.toggle() }
.animation(Animation.linear(duration: 5).repeatForever(autoreverses: true), value: go)
}
}
One way to do this is by using the "PreferenceKey" protocol, in conjunction with the "preference" and "onPreferenceChange" modifiers.
It's SwiftUI's way to send data from child Views to parent Views.
Code:
struct ContentView: View {
#State private var offset: CGFloat = 0.0
private let text: String = "Hello, World! My name is Oleg and I would like this text to slide!"
var body: some View {
// Capturing the width of the screen
GeometryReader { screenGeo in
ZStack {
Color.yellow
.frame(height: 50)
Text(text)
.fixedSize()
.background(
// Capturing the width of the text
GeometryReader { geo in
Color.red
// Sending width difference to parent View
.preference(key: MyTextPreferenceKey.self, value: screenGeo.size.width - geo.frame(in: .local).width
)
}
)
}
.offset(x: self.offset)
// Receiving width from child view
.onPreferenceChange(MyTextPreferenceKey.self, perform: { width in
withAnimation(Animation.easeInOut(duration: 1).repeatForever()) {
self.offset = width
}
})
}
}
}
// MARK: PreferenceKey
struct MyTextPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0.0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
Result:
Using some inspiration from you guys, I end up doing something way simpler by using only the Animation functions/properties.
import SwiftUI
struct SlidingText: View {
let geometryProxy: GeometryProxy
#Binding var text: String
let font: Font
#State private var animateSliding: Bool = false
private let slideDelay: Double = 3
private let slideDuration: Double = 6
private var isTextLargerThanView: Bool {
if text.size(forWidth: geometryProxy.size.width, andFont: font).width < geometryProxy.size.width {
return false
}
return true
}
var body: some View {
ZStack(alignment: .leading, content: {
VStack(content: {
Text(text)
.font(self.font)
.foregroundColor(.white)
})
.id("SlidingText-Animation")
.fixedSize()
.animation(isTextLargerThanView ? Animation.linear(duration: slideDuration).delay(slideDelay).repeatForever(autoreverses: true) : nil)
.frame(width: geometryProxy.size.width,
alignment: isTextLargerThanView ? (animateSliding ? .trailing : .leading) : .center)
.onAppear(perform: {
self.animateSliding.toggle()
})
})
.clipped()
}
}
And I call the SlidingView
GeometryReader(content: { geometry in
SlidingText(geometryProxy: geometry,
text: self.$playerViewModel.seasonByline,
font: .custom("FoundersGrotesk-RegularRegular", size: 15))
})
.frame(height: 20)
.fixedSize(horizontal: false, vertical: true)
Thank you again for your help!