当键盘出现在 SwiftUI 中时向上移动 TextField [英] Move TextField up when the keyboard has appeared in SwiftUI

查看:50
本文介绍了当键盘出现在 SwiftUI 中时向上移动 TextField的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的主 ContentView 中有七个 TextField.当用户打开键盘时,一些 TextField 隐藏在键盘框架下.所以我想在键盘出现时分别将所有 TextField 向上移动.

I have seven TextField inside my main ContentView. When user open keyboard some of the TextField are hidden under the keyboard frame. So I want to move all TextField up respectively when the keyboard has appeared.

我使用以下代码在屏幕上添加了 TextField.

I have used the below code to add TextField on the screen.

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"))
            }
    }
}

输出:

推荐答案

针对 Xcode, beta 7 更新了代码.

您不需要填充、滚动视图或列表来实现这一点.虽然这个解决方案也很适合他们.我在这里包括两个例子.

You do not need padding, ScrollViews or Lists to achieve this. Although this solution will play nice with them too. I am including two examples here.

第一个将所有 textField 向上移动,如果其中任何一个出现键盘.但只有在需要时.如果键盘不隐藏文本字段,它们将不会移动.

The first one moves all textField up, if the keyboard appears for any of them. But only if needed. If the keyboard doesn't hide the textfields, they will not move.

在第二个示例中,视图仅移动到足以避免隐藏活动文本字段的位置.

In the second example, the view only moves enough just to avoid hiding the active textfield.

这两个示例都使用了最后的相同代码:GeometryGetterKeyboardGuardian

Both examples use the same common code found at the end: GeometryGetter and KeyboardGuardian

struct ContentView: View {
    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 1)
    @State private var name = Array<String>.init(repeating: "", count: 3)

    var body: some View {

        VStack {
            Group {
                Text("Some filler text").font(.largeTitle)
                Text("Some filler text").font(.largeTitle)
            }

            TextField("enter text #1", text: $name[0])
                .textFieldStyle(RoundedBorderTextFieldStyle())

            TextField("enter text #2", text: $name[1])
                .textFieldStyle(RoundedBorderTextFieldStyle())

            TextField("enter text #3", text: $name[2])
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[0]))

        }.offset(y: kGuardian.slide).animation(.easeInOut(duration: 1.0))
    }

}

第二个示例(仅显示活动字段)

struct ContentView: View {
    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 3)
    @State private var name = Array<String>.init(repeating: "", count: 3)

    var body: some View {

        VStack {
            Group {
                Text("Some filler text").font(.largeTitle)
                Text("Some filler text").font(.largeTitle)
            }

            TextField("text #1", text: $name[0], onEditingChanged: { if $0 { self.kGuardian.showField = 0 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[0]))

            TextField("text #2", text: $name[1], onEditingChanged: { if $0 { self.kGuardian.showField = 1 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[1]))

            TextField("text #3", text: $name[2], onEditingChanged: { if $0 { self.kGuardian.showField = 2 } })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .background(GeometryGetter(rect: $kGuardian.rects[2]))

            }.offset(y: kGuardian.slide).animation(.easeInOut(duration: 1.0))
    }.onAppear { self.kGuardian.addObserver() } 
.onDisappear { self.kGuardian.removeObserver() }

}

GeometryGetter

这是一个吸收其父视图大小和位置的视图.为了实现这一点,它在 .background 修饰符中被调用.这是一个非常强大的修饰符,不仅仅是一种装饰视图背景的方法.将视图传递给 .background(MyView()) 时,MyView 将作为父视图获取修改后的视图.使用 GeometryReader 可以让视图知道父级的几何形状.

GeometryGetter

This is a view that absorbs the size and position of its parent view. In order to achieve that, it is called inside the .background modifier. This is a very powerful modifier, not just a way to decorate the background of a view. When passing a view to .background(MyView()), MyView is getting the modified view as the parent. Using GeometryReader is what makes it possible for the view to know the geometry of the parent.

例如:Text("hello").background(GeometryGetter(rect: $bounds)) 将填充变量边界,使用文本视图的大小和位置,并使用全局坐标空间.

For example: Text("hello").background(GeometryGetter(rect: $bounds)) will fill variable bounds, with the size and position of the Text view, and using the global coordinate space.

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { geometry in
            Group { () -> AnyView in
                DispatchQueue.main.async {
                    self.rect = geometry.frame(in: .global)
                }

                return AnyView(Color.clear)
            }
        }
    }
}

更新我添加了 DispatchQueue.main.async,以避免在渲染视图时修改视图状态的可能性.***

Update I added the DispatchQueue.main.async, to avoid the possibility of modifying the state of the view while it is being rendered.***

KeyboardGuardian 的目的是跟踪键盘显示/隐藏事件并计算视图需要移动多少空间.

The purpose of KeyboardGuardian, is to keep track of keyboard show/hide events and calculate how much space the view needs to be shifted.

更新: 我修改了 KeyboardGuardian 以在用户从一个字段切换到另一个字段时刷新幻灯片

import SwiftUI
import Combine

final class KeyboardGuardian: ObservableObject {
    public var rects: Array<CGRect>
    public var keyboardRect: CGRect = CGRect()

    // keyboardWillShow notification may be posted repeatedly,
    // this flag makes sure we only act once per keyboard appearance
    public var keyboardIsHidden = true

    @Published var slide: CGFloat = 0

    var showField: Int = 0 {
        didSet {
            updateSlide()
        }
    }

    init(textFieldCount: Int) {
        self.rects = Array<CGRect>(repeating: CGRect(), count: textFieldCount)

    }

    func addObserver() {
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)
}

func removeObserver() {
 NotificationCenter.default.removeObserver(self)
}

    deinit {
        NotificationCenter.default.removeObserver(self)
    }



    @objc func keyBoardWillShow(notification: Notification) {
        if keyboardIsHidden {
            keyboardIsHidden = false
            if let rect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect {
                keyboardRect = rect
                updateSlide()
            }
        }
    }

    @objc func keyBoardDidHide(notification: Notification) {
        keyboardIsHidden = true
        updateSlide()
    }

    func updateSlide() {
        if keyboardIsHidden {
            slide = 0
        } else {
            let tfRect = self.rects[self.showField]
            let diff = keyboardRect.minY - tfRect.maxY

            if diff > 0 {
                slide += diff
            } else {
                slide += min(diff, 0)
            }

        }
    }
}

这篇关于当键盘出现在 SwiftUI 中时向上移动 TextField的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆