Выдвинув один вид SwiftUI из-под другого - PullRequest
4 голосов
/ 17 июня 2019

Я пытаюсь создать анимацию, используя SwiftUI.

Start: [ A ][ B ][ D ]
End:   [ A ][ B ][    C    ][ D ]

Ключевые элементы анимации:

  • C должно выскользнуть из-под B (не расширяться от нулевой ширины)
  • Ширина всех видов определяется подпредставлениями и не известна
  • Ширина всех подпредставлений не должна изменяться во время или после анимации (таким образом, общая ширина представления больше в конечном состоянии)

Мне очень трудно удовлетворить все эти требования с помощью SwiftUI, но в прошлом мне удалось добиться аналогичных результатов с помощью автоматического макета.

Моей первой попыткой был переход с использованием HStack с layoutPriorities. Это не очень близко, потому что это влияет на ширину C во время анимации.

Моя вторая попытка состояла в том, чтобы сохранить HStack, но использовать переход с асимметричной анимацией движения. Это было очень близко, но движение B и C во время анимации не дает эффекта, когда C находился непосредственно под B.

Моей последней попыткой было отказаться от использования HStack для двух видов анимации и использовать вместо него ZStack. С помощью этой настройки я могу добиться идеальной анимации, используя комбинацию offset и padding. Однако , я могу сделать это правильно, только если я сделаю размеры кадра B и C. известными значениями.

Есть ли у кого-нибудь идеи о том, как добиться этого эффекта без , требующего фиксированных размеров кадров для B и C?

Ответы [ 2 ]

3 голосов
/ 24 июня 2019

С тех пор как я первоначально ответил на этот вопрос, я изучал GeometryReader, View Preferences и Anchor Preferences. Я собрал подробное объяснение, которое развивается далее. Вы можете прочитать его по адресу: https://swiftui -lab.com / общение с видом на дерево-часть-1 /

enter image description here

Как только вы добавили геометрию вида CCCCCCCC в переменную textRect, все остальное легко. Вы просто используете модификатор .offset (x :) и clipped ().

import SwiftUI

struct RectPreferenceKey: PreferenceKey {
    static var defaultValue = CGRect()

    static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
        value = nextValue()
    }

    typealias Value = CGRect
}

struct ContentView : View {
    @State private var textRect = CGRect()
    @State private var slideOut = false

    var body: some View {

        return VStack {
            HStack(spacing: 0) {
                Text("AAAAAA")
                    .font(.largeTitle)
                    .background(Color.yellow)
                    .zIndex(4)


                Text("BBBB")
                    .font(.largeTitle)
                    .background(Color.red)
                    .zIndex(3)

                Text("I am a very long text")
                    .zIndex(2)
                    .font(.largeTitle)
                    .background(GeometryGetter())
                    .background(Color.green)
                    .offset(x: slideOut ? 0.0 : -textRect.width)
                    .clipped()
                    .onPreferenceChange(RectPreferenceKey.self) { self.textRect = $0 }

                Text("DDDDDDDDDDDDD").font(.largeTitle)
                    .zIndex(1)
                    .background(Color.blue)
                    .offset(x: slideOut ? 0.0 : -textRect.width)

            }.offset(x: slideOut ? 0.0 : +textRect.width / 2.0)

            Divider()
            Button(action: {
                withAnimation(.basic(duration: 1.5)) {
                    self.slideOut.toggle()
                }
            }, label: {
                Text("Animate Me")
            })
        }

    }
}

struct GeometryGetter: View {
    var body: some View {
        GeometryReader { geometry in
            return Rectangle()
                .fill(Color.clear)
                .preference(key: RectPreferenceKey.self, value:geometry.frame(in: .global))
        }
    }
}
1 голос
/ 24 июня 2019

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

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

Animated GIF of my solution

Наблюдения:

  • Анимация основана на предположении, что (A) и (B) вместе шире, чем (C).В противном случае части (C) появятся слева от A в начале анимации.

  • Аналогично, анимация основывается на том факте, что между представлениями нет промежутков.В противном случае (C) будет отображаться слева от (B), если он шире (B).

    Возможно решить обе проблемы, разместив непрозрачный вид подложки в иерархии так, чтобы онниже (A), (B) и (D), но выше (C).Но я не продумал это до конца.

  • Кажется, HStack расширяется чуть быстрее, чем скользит (C), вот почему белая часть появляется на короткое время.Мне не удалось устранить это.Я попытался добавить тот же самый модификатор animation(.basic()) к HStack, переходу, вызову withAnimation и VStack, но это не помогло.

код:

import SwiftUI

struct ContentView: View {
  @State var thirdViewIsVisible: Bool = false

  var body: some View {
    VStack(alignment: .leading, spacing: 20) {
      HStack(spacing: 0) {
        Text("Lorem ").background(Color.yellow)
          .zIndex(1)
        Text("ipsum ").background(Color.red)
          .zIndex(1)
        if thirdViewIsVisible {
          Text("dolor sit ").background(Color.green)
            .zIndex(0)
            .transition(.move(edge: .leading))
        }
        Text("amet.").background(Color.blue)
          .zIndex(1)
      }
        .border(Color.red, width: 1)
      Button(action: { withAnimation { self.thirdViewIsVisible.toggle() } }) {
        Text("Animate \(thirdViewIsVisible ? "out" : "in")")
      }
    }
      .padding()
      .border(Color.green, width: 1)
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...