使用iOS SDK:

我有一个带UITextFields的UIView,可以启动键盘。我需要它能够:

启动键盘后,允许滚动UIScrollView的内容以查看其他文本字段自动“跳转”(通过向上滚动)或缩短

我知道我需要一个UIScrollView。我已经尝试将UIView的类更改为UIScrollView,但仍然无法上下滚动文本框。

我需要UIView和UIScrollView吗?一个在另一个里面吗?

要自动滚动到活动文本字段,需要执行哪些操作?

理想情况下,尽可能多的组件设置将在Interface Builder中完成。我只想编写需要的代码。

注意:我使用的UIView(或UIScrollView)是由一个选项卡(UITabBar)启动的,它需要正常工作。


我正在添加滚动条,只为键盘出现时使用。尽管不需要它,但我觉得它提供了一个更好的界面,例如,用户可以滚动和更改文本框。

当键盘上下移动时,我可以改变UIScrollView的框架大小。我只是在使用:

-(void)textFieldDidBeginEditing:(UITextField *)textField {
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x,
                                  scrollView.frame.origin.y,
    scrollView.frame.size.width,
    scrollView.frame.size.height - 215 + 50);   // Resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
    // Keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x,
                                  scrollView.frame.origin.y,
                                  scrollView.frame.size.width,
                                  scrollView.frame.size.height + 215 - 50); // Resize
}

然而,这不会自动“向上移动”或将可见区域中的下部文本字段居中,这是我真正想要的。


当前回答

一个更为优雅的解决方案是使用UIView子类(虽然这并不总是合适的),并在父帧更改时重新计算所有子视图(并且要聪明:只有在新帧大小发生更改时才重新计算它们,即在重写setFrame和调用[super setFrame:frame_]之前使用CGRectEqualToRect来比较新帧)。唯一的缺点是,您打算使用的UIViewController可能应该监听键盘事件(或者,您可以在UIView本身中进行监听,以方便封装)。但只有UIKeyboardWillShowNotification和UIKeyboard WillHideNotification。这只是为了让它看起来平滑(如果你等待CG调用它,你会得到一个波涛汹涌的时刻)。

无论如何,这有一个优点,那就是构建一个做正确事情的UIView子类。

天真的实现是重写drawRect:(不要),更好的方法是只使用layoutSubviews(然后在UIViewController中,或者在为显示或隐藏调用的SINGLE方法中调用[view setNeedsLayout])。

此解决方案摆脱了硬编码键盘偏移(如果它们不在拆分等中,则会发生变化),还意味着您的视图可以是许多其他视图的子视图,并且仍然可以正确响应。

除非没有其他解决方案,否则不要硬编码这样的东西。如果你做得对,操作系统会给你足够的信息,你只需要智能地重新绘制(基于你的新帧大小)。这要干净得多,而且是你应该做的事情。(不过,可能还有更好的方法。)

干杯

其他回答

使用这个第三方,你甚至不需要写一行

https://github.com/hackiftekhar/IQKeyboardManager

下载项目并在项目中拖放IQKeyboardManager。如果您发现任何问题,请阅读README文档。

伙计们管理键盘真的很头疼。

这是独立于设备的偏移计算。获取键盘和文本字段之间的重叠高度:

func keyboardShown(notification: NSNotification) {
    let info  = notification.userInfo!
    let value: AnyObject = info[UIKeyboardFrameEndUserInfoKey]!

    let rawFrame = value.CGRectValue
    let keyboardFrame = view.convertRect(rawFrame, fromView: nil)

    let screenHeight = UIScreen.mainScreen().bounds.size.height;
    let Ylimit = screenHeight - keyboardFrame.size.height
    let textboxOriginInSuperview:CGPoint = self.view.convertPoint(CGPointZero, fromCoordinateSpace: lastTextField!)

    self.keyboardHeight = (textboxOriginInSuperview.y+self.lastTextField!.frame.size.height) - Ylimit

    if(self.keyboardHeight>0){
        self.animateViewMoving(true, moveValue: keyboardHeight!)
    }else{
        keyboardHeight=0
    }
}

keyBoardHeight是偏移量。

-快速用户界面

仅显示活动文本字段

这将充分移动视图,以避免仅隐藏活动的TextField。

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: 0.25))
    }

}

显示所有文本字段

如果键盘显示其中任何一个文本字段,则会将所有文本字段上移。但只有在需要时。如果键盘不隐藏文本字段,它们将不会移动。

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: 0.25))
    }

}

两个示例都使用相同的通用代码:GeometryGetter和KeyboardGuardian灵感来自@kontiki

几何图形获取器

这是一个吸收其父视图的大小和位置的视图。在这里封装描述为了实现这一点,它在.background修饰符中被调用。这是一个非常强大的修改器,而不仅仅是一种装饰视图背景的方法。当将视图传递给.background(MyView())时,MyView将获得修改后的视图作为父视图。使用GeometryReader可以使视图了解父对象的几何图形。

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

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是为了避免在渲染视图时修改视图状态的可能性。

键盘守护者

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)

        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)

    }

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

        }
    }
}

我发现这是最好的解决方案,请遵循以下代码:

将以下内容附加到垂直空间-底部布局指南-文本字段约束。

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewBottomConst;

第二,为键盘通知添加观察员。

- (void)observeKeyboard {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

将此添加到您的视图DidLoad

[self observeKeyboard]; 

最后,处理键盘更改的方法。

- (void)keyboardWillShow:(NSNotification *)notification {
//THIS WILL MAKE SURE KEYBOARD DOESNT JUMP WHEN OPENING QUICKTYPE/EMOJI OR OTHER KEYBOARDS.
kbHeight = 0;
height = 0;
self.textViewBottomConst.constant = height;
self.btnViewBottomConst.constant = height;

    NSDictionary *info = [notification userInfo];
    NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];

    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGRect keyboardFrame = [kbFrame CGRectValue];

    CGRect finalKeyboardFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];

    int kbHeight = finalKeyboardFrame.size.height;

    int height = kbHeight + self.textViewBottomConst.constant;

    self.textViewBottomConst.constant = height;

    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];
    }];
}

- (void)keyboardWillHide:(NSNotification *)notification {
    NSDictionary *info = [notification userInfo];

    NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    self.textViewBottomConst.constant = 10;

    [UIView animateWithDuration:animationDuration animations:^{
        [self.view layoutIfNeeded];
    }];
}

在iOS中,向上移动键盘并收回应用程序中的文本字段有点令人困惑,需要实现一些相同的方法。此外,您还需要向textfield委派并处理它。它的代码将在存在文本字段的每个类中重复。

我更喜欢使用这个Github控件。

IQ键盘

在哪儿我们不需要做任何事情。——只需将拖放控件拖到项目中并构建。--它将为您的应用程序提供所有功能。

谢谢

也许这会有用。