Swiftui animation calendar dates - ios

In my app I want to show the passage of time by having a "calendar" transition from one date to the next, to the next, to the next, etc. So, for example, if I want to show the date transitioning from the 18th, to the 19th, to the 20th, I will show 18 for 1 second, then fade that out, fade in 19, fade that out, then fade in 20.
I have the following to show one date animating to the next (e.g. 18 > 19th):
struct Calendar: View {
#State var date: String
var body: some View {
VStack {
Spacer()
ZStack {
RoundedRectangle(cornerRadius: 20)
.stroke(Color.black, lineWidth: 2)
.frame(width: 200, height: 200)
RoundedRectangle(cornerRadius: 20)
.fill(Color.red)
.frame(width: 200, height: 200)
.offset(y: 160)
.clipped()
.offset(y: -160)
RoundedRectangle(cornerRadius: 20)
.stroke(Color.black, lineWidth: 2)
.frame(width: 200, height: 200)
.offset(y: 160)
.clipped()
.offset(y: -160)
Text(date).font(.system(size: 70.0))
.offset(y: 20)
}
Spacer()
Spacer()
}.padding()
}
}
and I call this in my code using:
ScrollView(showsIndicators: false) {
VStack {
Spacer()
ZStack {
if showseconddate == false {
Calendar(date: "18").animation(.easeInOut(duration: 1.0))
.transition(.opacity)
}
if showseconddate == true {
Calendar(date: "19").animation(.easeInOut(duration: 1.0))
.transition(.opacity)
}
Spacer()
}
}.onAppear {
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
withAnimation(Animation.linear(duration: 0.5)) {
self.showseconddate.toggle()
self.showfirstdate.toggle() }
timer.invalidate()
}
}
}
This all works as intended, but I'm struggling to then expand this to a case where I want to show it transitioning through multiple dates, such as 18 > 19 >20 >21 etc. Does anyone know how to expand this, or to use an alternative solution? Any solution must fade out the old date, then fade in the new date. Many thanks!

Here's a relatively compact solution. Instead of relying on Bool values, it cycles through an array:
struct ContentView: View {
private var dates = ["18","19","20","21","22"]
#State private var dateIndex = 0
private let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect()
var body: some View{
ScrollView(showsIndicators: false) {
VStack {
Spacer()
ZStack {
Calendar(date: dates[dateIndex])
.transition(.opacity)
.id("date-\(dateIndex)")
Spacer()
}
}.onReceive(timer) { _ in
var newIndex = dateIndex + 1
if newIndex == dates.count { newIndex = 0 }
withAnimation(.easeInOut(duration: 0.5)) {
dateIndex = newIndex
}
}
}
}
}

I had reworked your code to get the animations running, I felt it was a bit annoying to watch the entire calendar flash, so I reworked it into a CalendarPage (I renamed Calendar to CalendarPage because Calendar is a Type in Swift) and CalendarView that takes the date and overlays it on the page.
CalendarPage is your Calendar with the date var and Text() removed:
struct CalendarPage: View {
var body: some View {
VStack {
Spacer()
ZStack {
RoundedRectangle(cornerRadius: 20)
.stroke(Color.black, lineWidth: 2)
.frame(width: 200, height: 200)
RoundedRectangle(cornerRadius: 20)
.fill(Color.red)
.frame(width: 200, height: 200)
.offset(y: 160)
.clipped()
.offset(y: -160)
RoundedRectangle(cornerRadius: 20)
.stroke(Color.black, lineWidth: 2)
.frame(width: 200, height: 200)
.offset(y: 160)
.clipped()
.offset(y: -160)
}
Spacer()
Spacer()
}.padding()
}
}
CalendarView uses the timer to increment your dates until you reach the endDate, and it only effects the opacity of the date itself, not the whole calendar:
struct CalendarView: View {
#State var date: Int = 0
#State var animate = false
#State var calendarSize: CGFloat = 20
let endDate = 31
// This keeps the font size consistent regardless of the size of the calendar
var fontSize: CGFloat {
calendarSize * 0.45
}
private let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect()
var body: some View {
CalendarPage(date: date.description)
.overlay(alignment: .bottom) {
VStack {
Text(date.description)
.font(.system(size: fontSize))
.opacity(animate ? 1 : 0)
}
.frame(height: calendarSize * 0.8)
}
.frame(width: 200, height: 200)
.readSize(onChange: { size in
calendarSize = min(size.width, size.height)
})
.onReceive(timer) { _ in
date += 1
withAnimation(.linear(duration: 0.3)) {
animate = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
if date != endDate {
withAnimation(.linear(duration: 0.2)) {
animate = false
}
} else {
timer.upstream.connect().cancel()
}
}
}
}
}
I also used a preference key to compute the height of the CalendarPage (though I could have hard coded it) using this View extension from FiveStars blog
extension View {
func readSize(onChange: #escaping (CGSize) -> Void) -> some View {
background(
GeometryReader { geometryProxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: geometryProxy.size)
}
)
.onPreferenceChange(SizePreferenceKey.self, perform: onChange)
}
}
fileprivate struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {}
}

Related

My system has run out of application memory(app)

This problem appeared when i wrote Text("Today i made:"............), 68 line and i don't know how to fix it.
My thought, that probably problem with #AppStorage(in HomeView i have same variable.... var savedSalary = Double()). But in this View i decided to write #AppStorage with different variable, i just added one more "y" in the end and wrote(var savedSalaryy: Double = 8.75).
Restart didn't help me too.
Before the reading the code watch this screen
import SwiftUI
struct DayOnWorkView: View {
#State private var ifTapped = false
#State private var startTime = Date()
#State private var timerInt = 0 // was string
#AppStorage("SALARY_KEY") var savedSalaryy: Double = 8.75 // was savedSalary = Double()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
NavigationView {
ZStack {
BackgroundView()
VStack(alignment: .center) {
Button(action: {
self.ifTapped.toggle()
}, label: {
Image(systemName: ifTapped ? "person.crop.circle.badge.checkmark" : "person.crop.circle.badge.xmark")
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
.onTapGesture {
if !ifTapped {
timerInt = 0
startTime = Date()
}
ifTapped.toggle()
}
Text(ifTapped ? "Online" : "Offline")
.font(.system(size: 30, design: .rounded))
.onTapGesture {
if !ifTapped {
timerInt = 0
startTime = Date()
}
ifTapped.toggle()
}
})
.tint(ifTapped ? Color.green : Color.red)
.shadow(radius: 30)
.opacity(0.9)
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
.offset(y: 150)
}
VStack {
// timer
Text(String(timerInt))
.font(Font.system(.largeTitle, design: .monospaced))
.foregroundColor(Color.white)
.onReceive(timer) { _ in
if self.ifTapped {
timerInt = Int(Date().timeIntervalSince( self.startTime))
}
}
Text("Today i made: \(Double(Double(savedSalaryy) * Double(Int(timerInt))) / 3600, specifier: "%.2f")")
.offset(y: 150)
.foregroundColor(Color.white)
.bold()
}
}
}
}
}
// Text("Today i made \(Double(Double(savedSalaryy) * Double(Int(timerInt))) / 3600)") WORK WORK WORK
struct DayOnWorkView_Previews: PreviewProvider {
static var previews: some View {
DayOnWorkView()
}
}

Pause the timer with a button

I am making a timer but am unsure of how to pause it when the button is triggered.
I have attempted to make a boolean #State but I am yet unsure how to pause the timer when the button is triggered. Please review my code below...
struct TestView: View {
#State var isTimeStarted = false
#State var to: CGFloat = 0
#State var timeDuration = 60
#State var time = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
#State var isPaused = false
#State private var count = 0
var body: some View {
ZStack {
VStack {
ZStack {
Circle()
.trim(from: 0, to: self.to)
.stroke(LinearGradient(gradient: Gradient(colors: [Color.white, Color.white.opacity(0.2)]), startPoint: .topLeading, endPoint: .bottomTrailing), style: StrokeStyle(lineWidth: 15.6, lineCap: .round))
.shadow(radius: 8)
.rotationEffect(.degrees(90))
.rotation3DEffect(Angle(degrees: 180), axis: (x: 1, y: 0, z: 110))
.frame(width: 70, height: 70)
.animation(.easeOut)
.padding()
.padding(.leading, 10)
Text("\(self.timeDuration, specifier: formatTime())")
.font(.system(size: 17.5, design: .rounded))
.fontWeight(.semibold)
.foregroundColor(.white)
.padding(.leading, 10)
}
Button {
isPaused = true
} label: {
ZStack {
BlurView2(style: .systemThinMaterialDark)
.frame(width: 145, height: 45)
.background(Color.yellow)
.cornerRadius(30)
.padding(.horizontal)
Image(systemName: "pause")
.font(.title2)
.shadow(radius: 10)
.foregroundColor(.yellow)
}
}
}
}
.preferredColorScheme(.dark)
.onAppear {
self.timeDuration = 60
withAnimation(.default) {
self.to = 60
}
self.isTimeStarted = true
}
.onReceive(self.time, perform: { _ in
if self.timeDuration != 0 {
self.timeDuration -= 1
withAnimation(.default) {
self.to = CGFloat(self.timeDuration)/60
}
} else {
self.timeDuration = 60
self.to = 60
}
})
}
func formatTime() -> String {
let minutes = Int(timeDuration) / 60 % 60
let seconds = Int(timeDuration) % 60
return String(format: "%02i:%02i", minutes,seconds)
}
}
struct BlurView2: UIViewRepresentable {
var style: UIBlurEffect.Style
func makeUIView(context: Context) -> UIVisualEffectView {
let view = UIVisualEffectView(effect: UIBlurEffect(style: style))
return view
}
func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
}
}
Thi should be pretty simple. Keep your timer firing but return in your function if it should pause.
Add this to the start of your .onReceive function:
if isPaused{
return
}
and change your Button to:
isPaused.toggle()

How do I add a vertical swipe up gesture(to go to a different view) in a scrollview in SwiftUI

I designed a SwiftUI view which is a scrollview. Now I need to add a vertical swipe gesture to it which shall take it to a different view. I tried to do it using the tabView and adding a rotating effect of -90 degrees to it. But that rotates my original view too and that's not what I want. I couldn't find any relevant help in SwiftUI which deals with swiping up a scrollview to a new view.
Here's my code..
the vertical swipe I achieved using this. But my view get rotated. Setting other angles disappears the view somehow. I am new to SwiftUI, I am stuck on it for a week now.1
GeometryReader { proxy in
TabView {
ScrollView {
VStack(alignment: .center) {
ZStack(alignment: .leading) {
Image("Asset 13").resizable().frame(width: percentWidth(percentage: 100), height: percentHeight(percentage: 50), alignment: .top)
HStack {
Spacer()
Image("Asset 1")//.padding(.bottom, 130)
Spacer()
}.padding(.bottom, 150)
HStack {
VStack(spacing:2) {
Text("followers").foregroundColor(.white).padding(.leading, 20)
HStack {
Image("Asset 3")
Text("10.5k").foregroundColor(.white)
}
}
Spacer()
VStack {
Image("Asset 10").padding(.trailing)
Text("300K Review ").foregroundColor(.white)
}
}.background(Image("Asset 2").resizable().frame(width: percentWidth(percentage: 100), height: percentHeight(percentage: 6), alignment: .leading))
.padding(.top, 410)
HStack {
Spacer()
Image("Asset 14").resizable().frame(width: percentWidth(percentage: 50), height: percentHeight(percentage: 25), alignment: .center)
Spacer()
}.padding(.top, 390)
}
VStack(spacing: 4) {
Text("Karuna Ahuja | Yoga Instructor").font(Font.custom(FontName.bold, size: 22))
Text("12 Years of Experience with Bhartiya Yog Sansthan").tracking(-1).font(Font.custom(FontName.light, size: 16)).opacity(0.4)
}
Divider()
HStack {
ZStack {
Image("Asset 6").resizable().frame(width: percentWidth(percentage: 30), height: percentHeight(percentage: 12), alignment: .center)
VStack {
Image("Asset 5").resizable().frame(width: percentWidth(percentage: 8), height: percentHeight(percentage: 4), alignment: .center)
Text("245").font(Font.custom(FontName.bold, size: 16))
Text("Video").font(Font.custom(FontName.medium, size: 16)).opacity(0.5)
}
}
ZStack {
Image("Asset 6").resizable().frame(width: percentWidth(percentage: 30), height: percentHeight(percentage: 12), alignment: .center)
VStack {
Image("Asset 7").resizable().frame(width: percentWidth(percentage: 8), height: percentHeight(percentage: 4), alignment: .center)
Text("45").font(Font.custom(FontName.bold, size: 16))
Text("Live Class").font(Font.custom(FontName.medium, size: 16)).opacity(0.5)
}
}
ZStack {
Image("Asset 6").resizable().frame(width: percentWidth(percentage: 30), height: percentHeight(percentage: 12), alignment: .center)
VStack {
Image("Asset 9").resizable().frame(width: percentWidth(percentage: 8), height: percentHeight(percentage: 4), alignment: .center)
Text("245").font(Font.custom(FontName.bold, size: 16))
Text("Sessions").font(Font.custom(FontName.medium, size: 16)).opacity(0.5)
}
}
}
Divider()
Text("Shine bright so that your light leads other. I'm a fitness junkie, high-energy yoga instructor. Let's make fitness FUN!").font(Font.custom(FontName.normal, size: 16)).tracking(-1).opacity(0.7).padding([.leading,.trailing], 6)
VideoPlayer(player: AVPlayer(url: videoUrl))
.frame(height: 320)
Spacer()
}.gesture(DragGesture(minimumDistance: 20, coordinateSpace: .global)
.onEnded { value in
let horizontalAmount = value.translation.width as CGFloat
let verticalAmount = value.translation.height as CGFloat
if abs(horizontalAmount) > abs(verticalAmount) {
print(horizontalAmount < 0 ? "left swipe" : "right swipe")
} else {
print(verticalAmount < 0 ? "up swipe" : "down swipe")
}
})
}.edgesIgnoringSafeArea(.all)
.ignoresSafeArea()
Text("this")
Text("this")
Text("this")
// ForEach(colors, id: \.self) { color in
// color // Your cell content
// }
// .rotationEffect(.degrees(-90)) // Rotate content
// .frame(
// width: proxy.size.width,
// height: proxy.size.height
// )
}
.frame(
width: proxy.size.height, // Height & width swap
height: proxy.size.width
)
.rotationEffect(.degrees(90), anchor: .topLeading) // Rotate TabView
.offset(x: proxy.size.width) // Offset back into screens bounds
.tabViewStyle(
PageTabViewStyle(indexDisplayMode: .never)
)
}
The only pure SwiftUI way I see is to do your own ScrollView implementation, which is not too complicated. This example has two views on top of each other. If you drag the first view further up than to the middle of the screen, it swipes away to reveal the second view.
struct ContentView: View {
#State private var offset = CGFloat.zero
#State private var dragOffset = CGFloat.zero
#State private var viewHeight = CGFloat.zero
var body: some View {
GeometryReader { fullgeo in
ZStack(alignment: .top) {
SecondView()
// necessary for second view to resize individually
.frame(height: fullgeo.size.height)
ScrollingView()
.overlay( GeometryReader { geo in Color.clear.onAppear { viewHeight = geo.size.height }})
.offset(y: offset + dragOffset)
.gesture(DragGesture()
.onChanged { value in
dragOffset = value.translation.height
}
.onEnded { value in
withAnimation(.easeOut) {
dragOffset = .zero
offset += value.predictedEndTranslation.height
// if bottom dragged higher than 50% of screen > second view
if offset < -(viewHeight - fullgeo.size.height/2) {
dragOffset = -viewHeight
return
}
// else constrain to top / bottom of ScrollingView
offset = max(min(offset, 0), -(viewHeight - fullgeo.size.height))
}
}
)
}
}
}
}
struct ScrollingView: View {
var body: some View {
VStack {
Text("View Top").font(.headline)
ForEach(0..<10) { _ in
Text("Content")
.frame(width: 200, height: 100)
.background(.white)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.gray)
}
}
struct SecondView: View {
var body: some View {
Text("View Bottom").font(.headline)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.orange)
}
}
Use this code:
import SwiftUI
struct PullToRefreshView: View
{
private static let minRefreshTimeInterval = TimeInterval(0.2)
private static let triggerHeight = CGFloat(100)
private static let indicatorHeight = CGFloat(100)
private static let fullHeight = triggerHeight + indicatorHeight
let backgroundColor: Color
let foregroundColor: Color
let isEnabled: Bool
let onRefresh: () -> Void
#State private var isRefreshIndicatorVisible = false
#State private var refreshStartTime: Date? = nil
init(bg: Color = .neutral0, fg: Color = .neutral90, isEnabled: Bool = true, onRefresh: #escaping () -> Void)
{
self.backgroundColor = bg
self.foregroundColor = fg
self.isEnabled = isEnabled
self.onRefresh = onRefresh
}
var body: some View
{
VStack(spacing: 0)
{
LazyVStack(spacing: 0)
{
Color.clear
.frame(height: Self.triggerHeight)
.onAppear
{
if isEnabled
{
withAnimation
{
isRefreshIndicatorVisible = true
}
refreshStartTime = Date()
}
}
.onDisappear
{
if isEnabled, isRefreshIndicatorVisible, let diff = refreshStartTime?.distance(to: Date()), diff > Self.minRefreshTimeInterval
{
onRefresh()
}
withAnimation
{
isRefreshIndicatorVisible = false
}
refreshStartTime = nil
}
}
.frame(height: Self.triggerHeight)
indicator
.frame(height: Self.indicatorHeight)
}
.background(backgroundColor)
.ignoresSafeArea(edges: .all)
.frame(height: Self.fullHeight)
.padding(.top, -Self.fullHeight)
}
private var indicator: some View
{
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: foregroundColor))
.opacity(isRefreshIndicatorVisible ? 1 : 0)
}
}
sample:
ScrollView{
VStack(spacing:0) {
//top of scrollView
PullToRefreshView{
//todo
}
}
}

SwiftUI Custom TabView Indicator

I cannot center the indicator on which the page is located. I cannot ignore the views on the left and right. When the number of page increases, it is inevitable that the indicator of the page I am on will shift to the left when the indicator number on the right is more than the indicator number on the left.
struct OnboardingView: View {
var pages: [Page] = [
Page(page: PageOne()),
Page(page: PageTwo()),
Page(page: PageThree()),
]
#State var offset: CGFloat = 0
var body: some View {
ScrollView(.init()) {
TabView {
ForEach(pages.indices, id: \.self) { item in
if item == 0 {
AnyView(_fromValue: pages[item].page)
.overlay(
GeometryReader { proxy -> Color in
let minX = proxy.frame(in: .global).minX
DispatchQueue.main.async {
withAnimation(.default) {
self.offset = -minX
}
}
return Color.clear
}
.frame(width: 0, height: 0)
,alignment: .leading
)
} else {
AnyView(_fromValue: pages[item].page)
}
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.overlay(
HStack(spacing: 15) {
ForEach(pages.indices, id: \.self) { item in
ZStack {
Capsule()
.foregroundColor(Color.white)
.frame(width: getCurrentPageIndex() == item ? 20 : 7, height: 20)
.frame(maxWidth: getCurrentPageIndex() == item ? .infinity : nil, alignment: .center)
}
}
}
.padding(.horizontal)
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
.padding(.bottom, 10)
,alignment: .bottom
)
}
.ignoresSafeArea()
}
func getCurrentPageIndex() -> Int {
let index = Int(round(Double(offset / getScreenWidth())))
return index
}
func getOffset() -> CGFloat {
let progress = offset / getScreenWidth()
return 22 * progress
}
}
struct OnboardingView_Previews: PreviewProvider {
static var previews: some View {
OnboardingView()
}
}
struct Page: Identifiable {
var id = UUID()
var page: Any
}
extension View {
func getScreenWidth() -> CGFloat {
return UIScreen.main.bounds.width
}
}
.overlay(
HStack(spacing: 15) {
ForEach(pages.indices, id: \.self) { item in
ZStack {
Capsule()
.foregroundColor(Color.white)
.frame(width: getCurrentPageIndex() == item ? 20 : 7, height: 20)
}
}
}
.padding(.horizontal)
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
.padding(.bottom, 10), alignment: .bottom
)

SwiftUI: How to use withAnimation in a ScrollView?

I've been trying to animate a ScrollView using ScrollViewReader and withAnimation.
I can't figure out why these two animations are not working, either from Button or .onAppear?
import SwiftUI
struct ScrollView2: View {
#State private var scrollText = false
var body: some View {
ScrollViewReader { scrollView in
ScrollView {
Button("Scroll to bottom") {
withAnimation(.linear(duration: 30)) {
scrollView.scrollTo(99, anchor: .center)
}
}
ForEach(0..<100) { index in
Text(String(index))
.id(index)
}
.onAppear(perform: {
withAnimation(.linear(duration: 30)) {
scrollView.scrollTo(scrollText ? 99 : 1, anchor: .center)
}
scrollText.toggle()
})
}
}
}
}
It seems like duration doesn't work within withAnimation. Alternatively, I created a function that executes a repeating Timer that fires over 30 seconds, calling scrollTo withAnimation on each loop.
struct ScrollView2: View {
#State private var scrollText = false
var body: some View {
ScrollViewReader { scrollView in
ScrollView {
Button("Scroll to bottom") {
animateWithTimer(proxy: scrollView)
}
ForEach(0..<100) { index in
Text(String(index))
.id(index)
}
}
}
}
func animateWithTimer(proxy: ScrollViewProxy) {
let count: Int = 100
let duration: Double = 30.0
let timeInterval: Double = (duration / Double(count))
var counter = 0
let timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: true) { (timer) in
withAnimation(.linear) {
proxy.scrollTo(counter, anchor: .center)
}
counter += 1
if counter >= count {
timer.invalidate()
}
}
timer.fire()
}
}
Note: There is a delay when you press the button initially because when it tries to scrollTo the first ~40 of numbers, they are already high on the screen and the scrollView doesn't need to scroll anywhere to center them. You can update the timeInterval and counter variables as needed.
Because duration doesn't seem to work with withAnimation yet, I had to be a bit hacky to get the animation effect I wanted.
Here's what I did:
I added a ScrollViewReader to my ScrollView
I used ForEach and added IDs to my items in my ScrollView
Used an .offset and .animation modifiers to animate the
ScrollView itself (not the items in it)
Used .scrollTo within .onAppear to move at launch the ScrollView
to an item further away from the start to allow the user to both
scroll back and forward the items, even with the ScrollView being
itself animated from right to left
Here's what my code looks like:
import SwiftUI
import AVKit
struct ProView: View {
#State private var scrollText = false
var body: some View {
ZStack {
// VStack {
// Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1))
// .ignoresSafeArea(.all)
// }
//
// VStack {
//
// VideoPlayer(player: AVPlayer(url: Bundle.main.url(forResource: "wave-1", withExtension: "mp4")!)) {
// VStack {
// Image("pro-text")
// .resizable()
// .frame(width: 200, height: .infinity)
// .scaledToFit()
// }
// }
// .ignoresSafeArea(.all)
// .frame(width: .infinity, height: 300)
ScrollView(.horizontal, showsIndicators: false) {
ScrollViewReader { value in
HStack(spacing: 5) {
ForEach(0 ..< 100) { i in
HStack {
Image("benefit-1")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-2")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-3")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-4")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-5")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-6")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-7")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-8")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-9")
.resizable()
.frame(width: 120, height: 120)
Image("benefit-10")
.resizable()
.frame(width: 120, height: 120)
}
.id(i)
}
}
.offset(x: scrollText ? -10000 : 20)
.animation(Animation.linear(duration: 300).repeatForever(autoreverses: false))
.onAppear() {
value.scrollTo(50, anchor: .trailing)
scrollText.toggle()
}
}
}
Spacer()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ProView()
}
}

Resources