Suche…


Scrollen einer UIScrollView / UITableView bei Anzeige der Tastatur

Dort gibt es nur wenige Ansätze:

  1. 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);

}
  1. 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

animierte GIF-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 dem UITextField 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 Klassen

  • Ich 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 ein UITextField und verbinden Sie es mit einem IBOutlet mit Ihrem View-Controller. Nennen Sie es textField .

  • 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.

Screenshot des Eigentümers der Datei

Stellen Sie sicher, dass die benutzerdefinierte Klasse für die Stammansicht leer ist.

leerer Screenshot der Wurzelansicht

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;
    }];
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow