Как исчезнуть элемент массива?

У меня есть массив строк, и я пытаюсь анимировать каждую из них как изображение на экране, по одной.

Я делаю что-то, что:

  • Перебирает массив
  • Создает вид Text, который анимируется (с непрозрачностью и небольшим изменением положения).
  • Подождите немного, чтобы вы могли прочитать текст
  • Исчезает с непрозрачностью

Я сделал именно это с помощью этого кода:

Group {
    let dialogue = [
        "string1",
        "string2",
        "string3",
        "string4",
        "string5",
        "string6"
    ]
    
    ForEach(0..<dialogue.count, id: \.self) { index in
        Text("\(dialogue[index])")
            .frame(width: geometryProxy.size.width * 0.25, height: geometryProxy.size.width * 0.25, alignment: .center)
            .minimumScaleFactor(0.15)
            .multilineTextAlignment(.center)
            .foregroundStyle(.white)
            .font(.system(size: 25, weight: .medium, design: .serif))
            .background(.cyan.opacity(0.5))
            .opacity(hasPresentedDialogue ? 1 : 0)
            .offset(y: hasPresentedDialogue ? 0 : geometryProxy.size.height * 0.35 - geometryProxy.size.height * 0.375)
            .position(x: geometryProxy.size.width * 0.65, y: geometryProxy.size.height * 0.35)
            .animation(.easeInOut(duration: 0.75).delay(5 * Double(index)), value: hasPresentedDialogue)
    }
    .onAppear { withAnimation { hasPresentedDialogue.toggle() } }
}

Ну... почти только это. Я не могу понять, как заставить просмотры исчезнуть до того, как появится следующий. Прямо сейчас они все просто накапливаются друг на друге, что явно не идеально.

Как я могу сделать так, чтобы каждый элемент также исчезал до появления следующего элемента?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
94
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

У вас неправильная концепция показа диалогов один за другим. Поскольку вы показываете их внутри цикла ForEach, они появятся все сразу.

Я не упомянул ваш индивидуальный дизайн, но поделился концепцией показа каждого текста один раз и перехода к следующему тексту после определенной задержки. Надеюсь, вы сможете сделать вывод и добавить свой собственный дизайн позже.

import SwiftUI

struct ContentView: View {
    let dialogue = [
        "string1",
        "string2",
        "string3",
        "string4",
        "string5",
        "string6"
    ]
    
    let delay = 2 // 2 seconds
    @State var currentDialogIndex = 0

    var body: some View {
        Text(dialogue[currentDialogIndex])
            .font(.title)
            .onAppear(perform: {
                Timer.scheduledTimer(withTimeInterval: TimeInterval(delay), repeats: true) { _ in
                    withAnimation(.easeInOut(duration: TimeInterval(delay))) {
                        currentDialogIndex += 1
                        if currentDialogIndex >= dialogue.count {
                            currentDialogIndex = 0
                        }
                    }
                }
            })
    }
}

Спасибо. Я заставил его работать, но не могу понять, как заставить каждый из них двигаться вниз в качестве смещения. Только первый движется вниз.

Joe 25.02.2024 12:23

Для такого представления в шахматном порядке можно использовать PhaseAnimator:

struct ContentView: View {
    let dialogue = [
        "string1",
        "string2",
        "string3",
        "string4",
        "string5",
        "string6"
    ]

    @State private var hasPresentedDialogue = false

    var body: some View {
        GeometryReader { geometryProxy in
            let w = geometryProxy.size.width
            let h = geometryProxy.size.height

            PhaseAnimator(dialogue, trigger: hasPresentedDialogue) { string in
                Group { // needed for transitions to work
                    Text(string)
                        .frame(width: w * 0.25, height: w * 0.25)
                        .minimumScaleFactor(0.15)
                        .multilineTextAlignment(.center)
                        .foregroundStyle(.white)
                        .font(.system(size: 25, weight: .medium, design: .serif))
                        .background(.cyan.opacity(0.5))
                        .id(string) // needed for transitions to work
                        .transition(
                            .asymmetric(
                                insertion: .push(from: .top),
                                removal: .opacity
                            )
                        )
                }
            } animation: { _ in
                .easeInOut(duration: 0.75).delay(1) // delay(5)
            }
            .padding(.leading, w * 0.525)
            .padding(.top, (h * 0.35) - (w * 0.125))
            .onAppear { hasPresentedDialogue.toggle() }
        }
    }
}

Animation

Если вы попробуете приведенный выше код, вы обнаружите, что он не запускается и не останавливается так аккуратно, как на гифке. Это потому, что PhaseAnimator работает по-своему:

  • Начальная фаза начинается, как показано, еще до срабатывания триггера.
  • В конце цикла он возвращается к первой фазе.

Чтобы исправить это при записи gif, я использовал пустую строку в начале массива. Внутри замыкания я использовал оператор if для проверки пустой строки:

Group {
    if !string.isEmpty {
        // content as before
    }
}

Таким образом, первая фаза может быть полностью подавлена. Если вы решите использовать тот же подход, возможно, вам захочется подавить и задержку, связанную с анимацией (только для первой фазы).

Другие способы решения проблемы «возврата к началу» можно найти в ответах на сообщение Как использовать PhaseAnimator для создания анимации, которая не возвращается к первой фазе?.

Это тоже отличный вариант, я его рассмотрю. Спасибо.

Joe 25.02.2024 12:47

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