123

TextFieldmain の中に7 つありContentViewます。ユーザーがキーボードを開くと、一部がTextFieldキーボード フレームの下に隠されます。TextFieldなので、キーボードが表示されたらそれぞれ上に移動したいです。

以下のコードを使用TextFieldして画面に追加しました。

struct ContentView : View {
    @State var textfieldText: String = ""

    var body: some View {
            VStack {
                TextField($textfieldText, placeholder: Text("TextField1"))
                TextField($textfieldText, placeholder: Text("TextField2"))
                TextField($textfieldText, placeholder: Text("TextField3"))
                TextField($textfieldText, placeholder: Text("TextField4"))
                TextField($textfieldText, placeholder: Text("TextField5"))
                TextField($textfieldText, placeholder: Text("TextField6"))
                TextField($textfieldText, placeholder: Text("TextField6"))
                TextField($textfieldText, placeholder: Text("TextField7"))
            }
    }
}

出力:

出力

4

24 に答える 24

13

ScrollViewキーボードが表示されたときにコンテンツがスクロールできるように、キーボードのサイズの下パディングを追加して設定する必要があります。

NotificationCenterキーボードのサイズを取得するには、 を使用して、keyboards イベントに登録する必要があります。これを行うには、カスタム クラスを使用できます。

import SwiftUI
import Combine

final class KeyboardResponder: BindableObject {
    let didChange = PassthroughSubject<CGFloat, Never>()

    private var _center: NotificationCenter
    private(set) var currentHeight: CGFloat = 0 {
        didSet {
            didChange.send(currentHeight)
        }
    }

    init(center: NotificationCenter = .default) {
        _center = center
        _center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        _center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    deinit {
        _center.removeObserver(self)
    }

    @objc func keyBoardWillShow(notification: Notification) {
        print("keyboard will show")
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            currentHeight = keyboardSize.height
        }
    }

    @objc func keyBoardWillHide(notification: Notification) {
        print("keyboard will hide")
        currentHeight = 0
    }
}

BindableObject適合により、このクラスを として使用し、ビューの更新をトリガーすることができますState。必要に応じて、SwiftUI チュートリアルのチュートリアルを参照してBindableObjectください

それを取得したらScrollView、キーボードが表示されたときにそのサイズを縮小するように構成する必要があります。便宜上、これScrollViewをある種のコンポーネントにラップしました。

struct KeyboardScrollView<Content: View>: View {
    @State var keyboard = KeyboardResponder()
    private var content: Content

    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        ScrollView {
            VStack {
                content
            }
        }
        .padding(.bottom, keyboard.currentHeight)
    }
}

あとは、コンテンツを custom 内に埋め込むだけですScrollView

struct ContentView : View {
    @State var textfieldText: String = ""

    var body: some View {
        KeyboardScrollView {
            ForEach(0...10) { index in
                TextField(self.$textfieldText, placeholder: Text("TextField\(index)")) {
                    // Hide keyboard when uses tap return button on keyboard.
                    self.endEditing(true)
                }
            }
        }
    }

    private func endEditing(_ force: Bool) {
        UIApplication.shared.keyWindow?.endEditing(true)
    }
}

編集: キーボードが隠れているときのスクロール動作は本当に奇妙です。paddingアニメーションを使用してパディングを更新すると、これが修正されるか、スクロール ビューのサイズを調整する以外のものを使用することを検討する必要があります。

于 2019-06-07T15:52:14.363 に答える
3

Mark Krenek と Heiko が指摘したように、Apple はついに Xcode 12 beta 4 でこの問題に対処しているように見えました。事態は急速に進んでいます。2020 年 8 月 18 日に公開された Xcode 12ベータ 5のリリース ノートによると、「フォーム、リスト、および TextEditor は、キーボードの後ろにコンテンツを非表示にすることはなくなりました。(66172025)」。私はそれをダウンロードして、ベータ 5 シミュレーター(iPhone SE2) で簡単なテストを行い、数日前に開始したアプリで Form コンテナーを使用しました。

TextFieldに対して「そのまま機能する」ようになりました。SwiftUI は、カプセル化フォームに適切な下部パディングを自動的に提供して、キーボード用のスペースを確保します。そして、フォームを自動的に上にスクロールして、キーボードのすぐ上に TextField を表示します。ScrollView コンテナーは、キーボードが表示されたときにも適切に動作するようになりました。

ただし、Андрей Первушин さんがコメントで指摘したように、TextEditor には問題があります。Beta 5 & 6 では、カプセル化フォームに適切な下部パディングが自動的に提供され、キーボード用のスペースが確保されます。ただし、フォームを自動的に上にスクロールすることはありません。キーボードは TextEditor をカバーします。そのため、TextField とは異なり、ユーザーはフォームをスクロールして TextEditor を表示する必要があります。バグレポートを提出します。おそらくBeta 7で修正されるでしょう。とても近い…</p>

https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-14-beta-release-notes/

于 2020-08-19T14:43:45.097 に答える
1

これは、SwiftUI でキーボードを処理する方法です。覚えておくべきことは、それが接続されている VStack で計算を行っているということです。

ビューで修飾子として使用します。こちらです:

struct LogInView: View {

  var body: some View {
    VStack {
      // Your View
    }
    .modifier(KeyboardModifier())
  }
}

したがって、この修飾子に到達するには、まず、UIResponder の拡張機能を作成して、VStack で選択された TextField の位置を取得します。

import UIKit

// MARK: Retrieve TextField first responder for keyboard
extension UIResponder {

  private static weak var currentResponder: UIResponder?

  static var currentFirstResponder: UIResponder? {
    currentResponder = nil
    UIApplication.shared.sendAction(#selector(UIResponder.findFirstResponder),
                                    to: nil, from: nil, for: nil)
    return currentResponder
  }

  @objc private func findFirstResponder(_ sender: Any) {
    UIResponder.currentResponder = self
  }

  // Frame of the superview
  var globalFrame: CGRect? {
    guard let view = self as? UIView else { return nil }
    return view.superview?.convert(view.frame, to: nil)
  }
}

キーボードが TextField を隠すのを避けるために、Combine を使用して KeyboardModifier を作成できるようになりました。

import SwiftUI
import Combine

// MARK: Keyboard show/hide VStack offset modifier
struct KeyboardModifier: ViewModifier {

  @State var offset: CGFloat = .zero
  @State var subscription = Set<AnyCancellable>()

  func body(content: Content) -> some View {
    GeometryReader { geometry in
      content
        .padding(.bottom, self.offset)
        .animation(.spring(response: 0.4, dampingFraction: 0.5, blendDuration: 1))
        .onAppear {

          NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)
            .handleEvents(receiveOutput: { _ in self.offset = 0 })
            .sink { _ in }
            .store(in: &self.subscription)

          NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)
            .map(\.userInfo)
            .compactMap { ($0?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.size.height }
            .sink(receiveValue: { keyboardHeight in
              let keyboardTop = geometry.frame(in: .global).height - keyboardHeight
              let textFieldBottom = UIResponder.currentFirstResponder?.globalFrame?.maxY ?? 0
              self.offset = max(0, textFieldBottom - keyboardTop * 2 - geometry.safeAreaInsets.bottom) })
        .store(in: &self.subscription) }
        .onDisappear {
          // Dismiss keyboard
          UIApplication.shared.windows
            .first { $0.isKeyWindow }?
            .endEditing(true)

          self.subscription.removeAll() }
    }
  }
}
于 2020-04-07T13:49:31.347 に答える