Анимация SwiftUI не непрерывна после изменения вида

Вот код тестирования:

import SwiftUI
struct ContentView: View {
  @State private var pad: Bool = false
  @State private var showDot: Bool = true
  var body: some View {
    VStack {
      Button {showDot.toggle()} label: {Text("Toggle Show Dot")}
      Spacer().frame(height: pad ? 100 : 10)
      Circ(showDot: showDot)
      Spacer()
    }.onAppear {
      withAnimation(.linear(duration: 3).repeatForever()) {pad = true}
    }
  }
}

struct Circ: View {
  let showDot: Bool
  var body: some View {
    Circle().stroke().frame(height: 50).overlay {if showDot {Circle().frame(height: 20)}}
  }
}

Бывает, что после того, как я переключаю showDot, круг точек снова не находится в центре круга штриха! Как я могу это исправить? Представление Circ дано, я не могу изменить это представление!

Animista - анимация на ходу!
Animista - анимация на ходу!
Если вы веб-дизайнер или разработчик, вы знаете, что добавление анимации на ваш сайт может помочь сделать его более привлекательным и динамичным....
Повысьте уровень своего сайта с помощью анимации CSS и JavaScript: Пошаговое руководство
Повысьте уровень своего сайта с помощью анимации CSS и JavaScript: Пошаговое руководство
Если вы хотите добавить визуальный интерес к своему сайту, то внедрение анимации с помощью CSS и JavaScript может стать отличным способом сделать это....
1
0
35
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Редактировать

Если вы можете просто скрыть представление, см. решение 1, которое предпочтительнее. Если вам нужно перестроить представление, см. решение 2.

Решение 1

Замените условие if модификатором .opacity(), который читается как 1, когда showDot равно true.

Таким образом, точка не исчезает полностью, она есть, но ее просто не видно. Вы будете переключать видимость, а не сам вид.

Как это:

    @State private var pad: Bool = false
    @State private var showDot: Bool = true
    
    var body: some View {
        VStack {
            Button {
                showDot.toggle()
            } label: {
                Text("Toggle Show Dot")
            }
            
            Spacer()
                .frame(height: pad ? 100 : 10)
            
            Circle().stroke()
                .frame(height: 50)
                .overlay {
                    Circle()
                        .frame(height: 20)
                        .opacity(showDot ? 1 : 0)   // <- Here
                }
            
            Spacer()
            
        }
        .onAppear {
            withAnimation(.linear(duration: 3).repeatForever()) {pad = true}
        }
    }

Решение 2

Анимацию можно заменить таймером. Каждый раз, когда он срабатывает, он будет перемещать весь вид, изменяя высоту Spacer().

    
    // These variables will track the position and moving direction of the dot
    @State private var pos: CGFloat = 0
    @State private var movingUp = false
    
    @State private var showDot: Bool = true
    
    // This variable will change the position
    // This is a dummy iniatialization, the .onAppear modifier sets the real timer
    @State private var timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in }
        
    var body: some View {
        VStack {
            Button {
                showDot.toggle()
            } label: {
                Text("Toggle Show Dot")
            }
            
            Spacer()
                .frame(height: pos)

            Circle().stroke()
                .frame(height: 50)
                .overlay {
                    if showDot {
                        Circle().frame(height: 20)
                    }
                }
            
            Spacer()
            
        }
        .onAppear {
            
            // The timer interval will define the speed
            timer = Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { _ in
                moveCircle()
            }
        }
    }
    
    private func moveCircle() {
        if movingUp {
            if pos <= 0 {
                pos = 0
                movingUp = false
            } else {
                pos -= 1
            }
        } else {
            if pos >= 100 {
                pos = 100
                movingUp = true
            } else {
                pos += 1
            }
        }
    }

В моей реальной ситуации, когда showDot == false, я не могу получить «Точечный вид». Следовательно, метод непрозрачности не может применяться.

蘇哲聖 14.05.2022 16:52

@ 蘇哲聖, анимация применяется к существующим представлениям, если вы удалите представление, а затем вставите новое (поскольку оно новое), текущая анимация к нему не применяется, вам нужно остановить запущенную анимацию и перезапустить ее снова, чтобы применить к новому виду. . Так что это единственное решение для сценария, описанного в исходном вопросе... Или вам нужно обновить свой вопрос, добавив более точное описание того, что действительно необходимо в вашем приложении.

Asperi 14.05.2022 17:38

@ 蘇哲聖: см. отредактированное решение, если оно вам подходит.

HunterLion 14.05.2022 17:46

Метод таймера работает. Благодарю вас!

蘇哲聖 14.05.2022 18:36

Другие вопросы по теме