Разрыв SwiftUI TextField при анимации клавиатуры

Я создаю интерфейс в стиле чата с помощью SwiftUI с TextField внизу и List элементов над ним. Когда текстовое поле получает фокус и появляется клавиатура, поле и его фон не остаются прикрепленными к клавиатуре, а перемещаются немного вперед или назад. Вот пример:

Это было сделано с помощью safeAreaInset:

struct ContentView: View {
  @State private var message = ""
  
  var body: some View {
    List([Color.red, .orange, .yellow, .green, .blue, .indigo, .purple], id: \.self) { color in
      color
        .frame(height: 150)
        .listRowInsets(EdgeInsets())
    }
    .scrollContentBackground(.hidden)
    .safeAreaInset(edge: .bottom) {
      TextField("Chat", text: $message)
        .textFieldStyle(.roundedBorder)
        .padding()
        .background(.thinMaterial)
    }
  }
}

Я получаю аналогичные результаты, располагая их в VStack:

  var body: some View {
    VStack(spacing: 0) {
      List([Color.red, .orange, .yellow, .green, .blue, .indigo, .purple], id: \.self) { color in
        color
          .frame(height: 150)
          .listRowInsets(EdgeInsets())
      }
      .scrollContentBackground(.hidden)

      TextField("Chat", text: $message)
        .textFieldStyle(.roundedBorder)
        .padding()
        .background(.thinMaterial)
    }
  }

Время анимации не было бы большой проблемой, если бы фон за текстовым полем во время анимации распространялся на клавиатуру. Пробовал сделать это с помощью ignoresSafeArea(.all, edges: .bottom), но видимого эффекта это не дало.

Я обнаружил, что превосходный Callsheet Кейси Лисса нашел способ сделать это (он упомянул, что использовал SwiftUI для приложения), но я не уверен, как это сделать. Вот как выглядит Callsheet при наличии клавиатуры:

Кто-нибудь знает, как добиться такого поведения?


1
50
1

Ответ:

Решено

Заставлять TextField точно соответствовать анимации клавиатуры бесполезно, поскольку можно использовать сторонние клавиатуры или другие подобные замены клавиатуры, которые имеют собственную анимацию представления.

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

Представление, в котором вы хотите игнорировать безопасную область клавиатуры, — это именно фон. В вашем случае это тонкий материал. Поэтому материалу необходимо игнорировать безопасную зону.

ThinMaterial — это ShapeStyle, а не View, поэтому модификаторы ViewModifiers нельзя применять напрямую.
Вместо этого создайте представление самостоятельно и примените модификатор.

struct ContentView: View {
    …
    var body: some View {
        …
        .safeAreaInset(edge: .bottom) {
            TextField("Chat", text: $message)
                …
                .background {
                    Rectangle().fill(.thinMaterial).edgesIgnoringSafeArea(.all)
                }
        }
    }
}

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