iOS
Tastatur verwalten
Suche…
Scrollen einer UIScrollView / UITableView bei Anzeige der Tastatur
Dort gibt es nur wenige Ansätze:
- Sie können Benachrichtigungen zu Tastaturerscheinungsereignissen abonnieren und den Versatz manuell ändern:
//Swift 2.0+
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourVCClassName.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourVCClassName.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
}
}
}
func keyboardWillHide(notification: NSNotification) {
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
//Objective-C
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
if (userInfo) {
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardEndFrame.size.height, 0);
}
}
- (void)keyboardWillHide:(NSNotification *)notification {
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}
- Oder nutzen Sie fertige Lösungen wie TPKeyboardAvoidingTableView oder TPKeyboardAvoidingScrollView https://github.com/michaeltyson/TPKeyboardAvoiding
Schließen Sie eine Tastatur mit Tipp auf Anzeige ab
Wenn Sie eine Tastatur durch Antippen außerhalb der Tastatur ausblenden möchten, können Sie diesen Hacky-Trick verwenden (funktioniert nur mit Objective-C):
- (void)viewDidLoad {
[super viewDidLoad];
// dismiss keyboard when tap outside a text field
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self.view action:@selector(endEditing:)];
[tapGestureRecognizer setCancelsTouchesInView:NO];
[self.view addGestureRecognizer:tapGestureRecognizer];
}
Für Swift wird es etwas mehr Code geben:
override func viewDidLoad() {
super.viewDidLoad()
// dismiss keyboard when tap outside a text field
let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourVCName.dismissKeyboard))
view.addGestureRecognizer(tapGestureRecognizer)
}
//Calls this function when the tap is recognized.
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
Ein weiteres Swift 3 / iOS 10-Beispiel
class vc: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
txtSomeField.delegate = self
}
}
extension vc: UITextFieldDelegate {
//Hide the keyboard for any text field when the UI is touched outside of the keyboard.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
self.view.endEditing(true) //Hide the keyboard
}
}
Erstellen Sie eine benutzerdefinierte In-App-Tastatur
Dies ist eine grundlegende In-App-Tastatur. Die gleiche Methode kann verwendet werden, um nahezu jedes Tastaturlayout zu erstellen. Hier sind die wichtigsten Dinge, die getan werden müssen:
- Erstellen Sie das Tastaturlayout in einer .xib-Datei, deren Besitzer eine Swift- oder Objective-C-Klasse ist, bei der es sich um eine
UIView
Unterklasse handelt. -
UITextField
Sie demUITextField
mit, die benutzerdefinierte Tastatur zu verwenden. - Verwenden Sie einen Delegaten, um zwischen der Tastatur und dem Hauptansicht-Controller zu kommunizieren.
Erstellen Sie die .xib-Tastaturlayoutdatei
- In Xcode gehen Sie zu Datei> Neu> Datei ...> iOS> Benutzeroberfläche> Ansicht , um die .xib-Datei zu erstellen.
- Ich habe meine Keyboard.xib angerufen
- Fügen Sie die Schaltflächen hinzu, die Sie benötigen.
- Verwenden Sie Auto-Layout-Einschränkungen, damit die Größe der Tasten unabhängig von der Größe der Tastatur entsprechend angepasst wird.
- Legen Sie den Eigentümer der Datei (nicht die Stammansicht) als
Keyboard
Klasse fest. Dies ist eine häufige Fehlerquelle. Sie erstellen diese Klasse im nächsten Schritt. Siehe die Notiz am Ende.
Erstellen Sie die .swift UIView-Unterklassen-Tastaturdatei
In Xcode gehen Sie zu Datei> Neu> Datei ...> iOS> Quelle> Cocoa Touch-Klasse , um die Swift- oder Objective-C- Klasse zu erstellen. Wählen Sie
UIView
als Superklasse für neu erstellte KlassenIch habe meine
Keyboard.swift
(Keyboard
Klasse in Objective-C) angerufen.Fügen Sie den folgenden Code für Swift hinzu:
import UIKit // The view controller will adopt this protocol (delegate) // and thus must contain the keyWasTapped method protocol KeyboardDelegate: class { func keyWasTapped(character: String) } class Keyboard: UIView { // This variable will be set as the view controller so that // the keyboard can send messages to the view controller. weak var delegate: KeyboardDelegate? // MARK:- keyboard initialization required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) initializeSubviews() } override init(frame: CGRect) { super.init(frame: frame) initializeSubviews() } func initializeSubviews() { let xibFileName = "Keyboard" // xib extention not included let view = NSBundle.mainBundle().loadNibNamed(xibFileName, owner: self, options: nil)[0] as! UIView self.addSubview(view) view.frame = self.bounds } // MARK:- Button actions from .xib file @IBAction func keyTapped(sender: UIButton) { // When a button is tapped, send that information to the // delegate (ie, the view controller) self.delegate?.keyWasTapped(sender.titleLabel!.text!) // could alternatively send a tag value } }
Fügen Sie den folgenden Code für Objective-C hinzu:
Keyboard.h-Datei
#import <UIKit/UIKit.h> // The view controller will adopt this protocol (delegate) // and thus must contain the keyWasTapped method @protocol KeyboardDelegate<NSObject> - (void)keyWasTapped:(NSString *)character; @end @interface Keyboard : UIView @property (nonatomic, weak) id<KeyboardDelegate> delegate; @end
Keyboard.m-Datei
#import "Keyboard.h" @implementation Keyboard - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; [self initializeSubviews]; return self; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; [self initializeSubviews]; return self; } - (void)initializeSubviews { NSString *xibFileName = @"Keyboard"; // xib extention not included UIView *view = [[[NSBundle mainBundle] loadNibNamed:xibFileName owner:self options:nil] firstObject]; [self addSubview:view]; view.frame = self.bounds; } // MARK:- Button actions from .xib file -(IBAction)keyTapped:(UIButton *)sender { // When a button is tapped, send that information to the // delegate (ie, the view controller) [self.delegate keyWasTapped:sender.titleLabel.text]; // could alternatively send a tag value } @end
- Steuern Sie die Drag-Aktionen von den Schaltflächen zum Schaltflächenrückruf in der .xib-Datei auf die
@IBAction
Methode im Besitzer von Swift oder Objective-C, um sie alle miteinander zu verbinden. - Beachten Sie, dass der Protokoll- und Delegierungscode. In dieser Antwort finden Sie eine einfache Erklärung zur Funktionsweise von Delegierten.
Richten Sie den View Controller ein
Fügen
UITextField
Ihrem Haupt-Storyboard einUITextField
und verbinden Sie es mit einemIBOutlet
mit Ihrem View-Controller. Nennen Sie estextField
.Verwenden Sie den folgenden Code für den View Controller in Swift:
import UIKit class ViewController: UIViewController, KeyboardDelegate { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() // initialize custom keyboard let keyboardView = Keyboard(frame: CGRect(x: 0, y: 0, width: 0, height: 300)) keyboardView.delegate = self // the view controller will be notified by the keyboard whenever a key is tapped // replace system keyboard with custom keyboard textField.inputView = keyboardView } // required method for keyboard delegate protocol func keyWasTapped(character: String) { textField.insertText(character) } }
Verwenden Sie den folgenden Code für Objective-C:
.h Datei
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
.m Datei
#import "ViewController.h" #import "Keyboard.h" @interface ViewController ()<KeyboardDelegate> @property (nonatomic, weak) IBOutlet UITextField *textField; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // initialize custom keyboard Keyboard *keyboardView = [[Keyboard alloc] initWithFrame:CGRectMake(0, 0, 0, 300)]; keyboardView.delegate = self; // the view controller will be notified by the keyboard whenever a key is tapped // replace system keyboard with custom keyboard self.textField.inputView = keyboardView; } - (void)keyWasTapped:(NSString *)character { [self.textField insertText:character]; } @end
- Beachten Sie, dass der View Controller das
KeyboardDelegate
Protokoll verwendet, das wir oben definiert haben.
Häufiger Fehler
Wenn Sie einen EXC_BAD_ACCESS
Fehler erhalten, liegt dies wahrscheinlich daran, dass Sie die benutzerdefinierte Klasse der Ansicht als Keyboard
EXC_BAD_ACCESS
, anstatt dies für den Besitzer der Nib-Datei zu tun.
Wählen Sie Keyboard.nib
und wählen Sie dann den Eigentümer der Datei.
Stellen Sie sicher, dass die benutzerdefinierte Klasse für die Stammansicht leer ist.
Anmerkungen
Dieses Beispiel stammt ursprünglich aus dieser Stack Overflow-Antwort .
Verwalten der Tastatur mit einem Singleton + -Delegierten
Als ich anfing, die Tastatur zu verwalten, verwendete ich in jedem ViewController separate Benachrichtigungen.
Benachrichtigungsmethode (mit NSNotification):
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardNotification(_:)), name: UIKeyboardWillChangeFrameNotification, object: nil)
}
func keyboardNotification(notification: NSNotification) {
guard let userInfo = notification.userInfo else { return }
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
let duration: NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseOut.rawValue
let animationCurve: UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
if endFrame?.origin.y >= UIScreen.mainScreen().bounds.size.height {
lowerViewBottomConstraint.constant = 0
} else {
lowerViewBottomConstraint.constant = endFrame?.size.height ?? 0.0
}
view.animateConstraintWithDuration(duration, delay: NSTimeInterval(0), options: animationCurve, completion: nil)
}
}
Mein Problem war, dass ich diesen Code immer wieder für jeden einzelnen ViewController schrieb. Nachdem ich ein wenig experimentiert hatte, fand ich mit einem Singleton + Delegate-Muster die Möglichkeit, eine Reihe von Code wiederzuverwenden und das gesamte Keyboard Management an einem einzigen Ort zu organisieren!
Singleton + Delegate-Methode:
protocol KeyboardManagerDelegate: class {
func keyboardWillChangeFrame(endFrame: CGRect?, duration: NSTimeInterval, animationCurve: UIViewAnimationOptions)
}
class KeyboardManager {
weak var delegate: KeyboardManagerDelegate?
class var sharedInstance: KeyboardManager {
struct Singleton {
static let instance = KeyboardManager()
}
return Singleton.instance
}
init() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(KeyboardManager.keyboardWillChangeFrameNotification(_:)), name: UIKeyboardWillChangeFrameNotification, object: nil)
}
@objc func keyboardWillChangeFrameNotification(notification: NSNotification) {
guard let userInfo = notification.userInfo else { return }
let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
let duration: NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseOut.rawValue
let animationCurve: UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
delegate?.keyboardWillChangeFrame(endFrame, duration: duration, animationCurve: animationCurve)
}
}
Wenn ich nun die Tastatur von einem ViewController aus verwalten möchte, muss ich nur den Delegierten auf diesen ViewController setzen und beliebige Delegatmethoden implementieren.
class ViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
KeyboardManager.sharedInstance.delegate = self
}
}
// MARK: - Keyboard Manager
extension ViewController: KeyboardManagerDelegate {
func keyboardWillChangeFrame(endFrame: CGRect?, duration: NSTimeInterval, animationCurve: UIViewAnimationOptions) {
if endFrame?.origin.y >= UIScreen.mainScreen().bounds.size.height {
lowerViewBottomConstraint.constant = 0
} else {
lowerViewBottomConstraint.constant = (endFrame?.size.height ?? 0.0)
}
view.animateConstraintWithDuration(duration, delay: NSTimeInterval(0), options: animationCurve, completion: nil)
}
}
Diese Methode ist auch sehr anpassbar! UIKeyboardWillHideNotification
wir möchten die Funktionalität für UIKeyboardWillHideNotification
hinzufügen. Dies ist so einfach wie das Hinzufügen einer Methode zu unserem KeyboardManagerDelegate
.
KeyboardManagerDelegate
mit UIKeyboardWillHideNotification
:
protocol KeyboardManagerDelegate: class {
func keyboardWillChangeFrame(endFrame: CGRect?, duration: NSTimeInterval, animationCurve: UIViewAnimationOptions)
func keyboardWillHide(notificationUserInfo: [NSObject: AnyObject])
}
class KeyboardManager {
init() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(KeyboardManager.keyboardWillChangeFrameNotification(_:)), name: UIKeyboardWillChangeFrameNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(KeyboardManager.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillHide(notification: NSNotification) {
guard let userInfo = notification.userInfo else { return }
delegate?.keyboardWillHide(userInfo)
}
}
func keyboardWillHide(notificationUserInfo: [NSObject: AnyObject])
wir möchten nur func keyboardWillHide(notificationUserInfo: [NSObject: AnyObject])
in einem ViewController implementieren. Wir können diese Methode auch optional machen.
typealias KeyboardManagerDelegate = protocol<KeyboardManagerModel, KeyboardManagerConfigureable>
protocol KeyboardManagerModel: class {
func keyboardWillChangeFrame(endFrame: CGRect?, duration: NSTimeInterval, animationCurve: UIViewAnimationOptions)
}
@objc protocol KeyboardManagerConfigureable {
optional func keyboardWillHide(userInfo: [NSObject: AnyObject])
}
* Beachten Sie, dass dieses Muster den @objc
von @objc
. Siehe http://www.jessesquires.com/avoiding-objc-in-swift/ für weitere Details!
Zusammenfassend habe ich festgestellt, dass die Verwendung eines Singleton + -Delegierten zur Verwaltung der Tastatur sowohl effizienter als auch einfacher zu verwenden ist als die Verwendung von Benachrichtigungen
Ansicht nach oben oder unten verschieben, wenn Tastatur vorhanden ist
Hinweis: Dies funktioniert nur für die von iOS bereitgestellte integrierte Tastatur
SCHNELL:
Damit die Ansicht eines UIViewController den Ursprung des Frames bei der Darstellung erhöht und beim Ausblenden verringert, fügen Sie Ihrer Klasse folgende Funktionen hinzu:
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
}
viewDidLoad()
in der viewDidLoad()
Methode Ihrer Klasse die folgenden Beobachter hinzu:
NotificationCenter.default.addObserver(self, selector: #selector(Login.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(Login.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Dies funktioniert bei jeder Bildschirmgröße und verwendet die Height-Eigenschaft der Tastatur.
ZIEL C:
Um das gleiche in Objective-C zu tun, kann dieser Code verwendet werden:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification
{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
[UIView animateWithDuration:0.3 animations:^{
CGRect f = self.view.frame;
f.origin.y = -keyboardSize.height;
self.view.frame = f;
}];
}
-(void)keyboardWillHide:(NSNotification *)notification
{
[UIView animateWithDuration:0.3 animations:^{
CGRect f = self.view.frame;
f.origin.y = 0.0f;
self.view.frame = f;
}];
}