iOS
Hantera tangentbordet
Sök…
Rulla en UIScrollView / UITableView när tangentbordet visas
Det finns få tillvägagångssätt tillgängliga där:
- Du kan prenumerera på meddelanden om händelser om utseende på tangentbord och ändra offset manuellt:
//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);
}
- Eller använd färdiga lösningar som TPKeyboardAvoidingTableView eller TPKeyboardAvoidingScrollView https://github.com/michaeltyson/TPKeyboardAvoiding
Ta bort ett tangentbord med tryckknappen
Om du vill dölja ett tangentbord genom att trycka utanför det är det möjligt att använda detta hackiga trick (fungerar bara med Objekt-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 kommer det att finnas lite mer kod:
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)
}
Ett annat exempel på Swift 3 / iOS 10
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
}
}
Skapa ett anpassat tangentbord i appen
Detta är ett grundläggande tangentbord i appen. Samma metod kan användas för att skapa nästan vilken tangentbordslayout som helst. Här är de viktigaste sakerna som måste göras:
- Skapa tangentbordlayouten i en .xib-fil, vars ägare är en snabb eller objektiv-C-klass som är en
UIView
underklass. -
UITextField
att använda det anpassade tangentbordet. - Använd en delegat för att kommunicera mellan tangentbordet och huvudvisningsstyrenheten.
Skapa .xib-tangentbordslayout
- I Xcode går du till File> New> File ...> iOS> User Interface> View för att skapa .xib-filen.
- Jag ringde mitt Keyboard.xib
- Lägg till knapparna du behöver.
- Använd begränsningar för automatisk layout så att oavsett vilken storlek tangentbordet är, kommer knapparna att ändra storlek därefter.
- Ställ in filens ägare (inte rotvyn) till klassen
Keyboard
. Detta är en vanlig felkälla. Du skapar den här klassen i nästa steg. Se noten i slutet.
Skapa .swift UIView underklass tangentbord
I Xcode går du till Arkiv> Ny> Fil ...> iOS> Källa> Cocoa Touch Class för att skapa Swift- eller Objekt-C-klassen. Välj
UIView
som en superklass för nyskapad klassJag kallade mine
Keyboard.swift
(Keyboard
class i Objekt-C)Lägg till följande kod för Swift:
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 } }
Lägg till följande kod för Objekt-C:
Keyboard.h-fil
#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-fil
#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
- Kontrollera dragåtgärder från knapparna för att knyta återuppringning i .xib-filen till
@IBAction
metoden i@IBAction
eller Objekt-C-ägaren för att ansluta dem alla. - Observera att protokollet och delegera koden. Se detta svar för en enkel förklaring om hur delegater arbetar.
Ställ in View Controller
Lägg till en
UITextField
på din huvud storyboard och anslut den till din visningskontroller med enIBOutlet
. Kall dettextField
.Använd följande kod för View Controller i 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) } }
Använd följande kod för Objekt-C:
.h-fil
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
.m-fil
#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
- Observera att visningskontrollern antar
KeyboardDelegate
protokollet som vi definierade ovan.
Vanligt fel
Om du får ett EXC_BAD_ACCESS
fel EXC_BAD_ACCESS
det troligen på att du anger EXC_BAD_ACCESS
visningsklass som Keyboard
stället för att göra detta för nibfilens ägare.
Välj Keyboard.nib
och välj sedan File's Owner.
Se till att den anpassade klassen för rotvyn är tom.
anteckningar
Detta exempel kommer ursprungligen från detta Stack Overflow-svar .
Hantera tangentbordet med hjälp av en Singleton + delegat
När jag först började hantera tangentbordet skulle jag använda separata meddelanden i varje ViewController.
Anmälningsmetod (med hjälp av 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)
}
}
Mitt problem var att jag tyckte att jag skrev denna kod om och om igen för varje enskild ViewController. Efter att ha experimenterat lite såg jag att jag använde ett Singleton + Delegate-mönster som tillät mig att återanvända ett gäng kod och organisera hela Keyboard Management på ett enda ställe!
Singleton + delegatmetod:
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)
}
}
När jag nu vill hantera tangentbordet från en ViewController behöver jag bara ställa in delegaten till ViewController och implementera alla delegerade metoder.
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)
}
}
Denna metod är också mycket anpassningsbar! Säg att vi vill lägga till funktionalitet för UIKeyboardWillHideNotification
. Detta är lika enkelt som att lägga till en metod till vårt KeyboardManagerDelegate
.
KeyboardManagerDelegate
med 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)
}
}
Säg att vi bara vill implementera func keyboardWillHide(notificationUserInfo: [NSObject: AnyObject])
i en ViewController. Vi kan också göra denna metod valfri.
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])
}
* Observera att detta mönster hjälper till att undvika överanvändning av @objc
. Se http://www.jessesquires.com/avoiding-objc-in-swift/ för mer information!
Sammanfattningsvis har jag funnit att använda en Singleton + Delegate för att hantera tangentbordet är både effektivare och enklare att använda än att använda Meddelanden
Flytta vyn upp eller ner när tangentbordet finns
Obs: Det här fungerar bara för det inbyggda tangentbordet från iOS
SNABB:
För att vyn för en UIViewController ska öka ursprunget till ramen när den presenteras och minska den när den är dold, lägg till följande funktioner i din klass:
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
}
}
}
Och i viewDidLoad()
för din klass lägger du till följande observatörer:
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)
Och detta fungerar för alla skärmstorlekar med hjälp av tangentbordets höjdegenskap.
MÅL-C:
För att göra samma sak i Objekt-C kan den här koden användas:
- (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;
}];
}