サーチ…
キーボードを表示するときのUIScrollView / UITableViewのスクロール
利用できるアプローチはいくつかあります。
- キーボードアピアランスイベント通知を購読し、オフセットを手動で変更することができます。
//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);
}
- または、TPKeyboardAvoidingTableViewやTPKeyboardAvoidingScrollViewといった既成のソリューションを使用してください。https://github.com/michaeltyson/TPKeyboardAvoiding
タップでキーボードを閉じる
キーボードの外側をタップしてキーボードを隠したいのであれば、このハックトリックを使用することができます(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];
}
Swiftにはもう少しコードがあります:
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)
}
別の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
}
}
カスタムアプリ内キーボードを作成する
これは、基本的なアプリ内キーボードです。同じ方法を使用して、ほぼすべてのキーボードレイアウトを作成することができます。実行する必要がある主な事項は次のとおりです。
- 所有者であるスウィフトまたはObjective-Cのクラスである.xibファイル内のキーボードレイアウト作成
UIView
サブクラス。 - カスタムキーボードを使用するように
UITextField
します。 - デリゲートを使用して、キーボードとメインビューコントローラ間の通信を行います。
.xibキーボードレイアウトファイルを作成する
- Xcodeでファイル>新規>ファイル...> iOS>ユーザーインターフェイス>表示に進み、.xibファイルを作成します。
- 私は私のKeyboard.xibと呼んだ
- 必要なボタンを追加します。
- キーボードのサイズに関係なく、ボタンのサイズが自動的に変更されるように、自動レイアウトの制約を使用します。
- ファイルの所有者(ルートビューではなく)を
Keyboard
クラスに設定します。これは一般的なエラーの原因です。次のステップでこのクラスを作成します。最後の注記を参照してください。
.swift UIViewサブクラスのキーボードファイルを作成する
Xcodeでファイル>新規>ファイル...> iOS>ソース> Cocoa Touch Classに移動して、SwiftまたはObjective-Cクラスを作成します。新しく作成されたクラスのスーパークラスとして
UIView
を選択私は私の
Keyboard.swift
(Objective-CのKeyboard
クラス)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 } }
Objective-Cの次のコードを追加します。
Keyboard.hファイル
#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ファイル
#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
- .xibファイル内のボタンからボタンコールバックをSwiftまたはObjective-Cオーナーの
@IBAction
メソッドにドラッグ操作して、それらをすべてフックします。 - プロトコルとデリゲートコードに注意してください。デリゲートの仕組みの簡単な説明については、 この回答を参照してください。
View Controllerを設定する
メインのストーリーボードに
UITextField
を追加し、IBOutlet
を使用してView Controllerに接続します。それをtextField
呼んでください。SwiftのView Controllerに次のコードを使用します。
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) } }
Objective-Cには次のコードを使用します。
.hファイル
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
.mファイル
#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
- View Controllerは、上で定義した
KeyboardDelegate
プロトコルを採用しています。
一般的なエラー
EXC_BAD_ACCESS
エラーが発生した場合は、nib Fileの所有者ではなく、ビューのカスタムクラスをKeyboard
として設定している可能性があります。
Keyboard.nib
を選択し、ファイルの所有者を選択します。
ルートビューのカスタムクラスが空白であることを確認します。
ノート
この例は、 このスタックオーバーフローの答えから元々来ます 。
シングルトン+デリゲートを使用したキーボードの管理
キーボードの管理を始めたとき、私は各ViewControllerで別々の通知を使用します。
通知メソッド(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)
}
}
私の問題は、すべての単一のViewControllerに対してこのコードを何度も書いていることに気づいたことです。私がシングルトン+デリゲートパターンを使って見つけたビットを試してみたら、たくさんのコードを再利用し、すべてのキーボード管理を一箇所で整理できました!
シングルトン+デリゲートメソッド:
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)
}
}
今私がViewControllerからキーボードを管理したいときは、そのViewControllerにデリゲートを設定し、デリゲートメソッドを実装するだけです。
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)
}
}
この方法も非常にカスタマイズ可能です! UIKeyboardWillHideNotification
機能を追加したいとしUIKeyboardWillHideNotification
。これは、 KeyboardManagerDelegate
メソッドを追加するのと同じくらい簡単です。
UIKeyboardWillHideNotification
KeyboardManagerDelegate
:
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)
}
}
1つのViewControllerにfunc keyboardWillHide(notificationUserInfo: [NSObject: AnyObject])
を実装したいとします。このメソッドをオプションにすることもできます。
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])
}
*このパターンは@objc
過度の使用を避けるのに役立ちます。詳細はhttp://www.jessesquires.com/avoiding-objc-in-swift/を参照してください。
要約すると、シングルトン+デリゲートを使用してキーボードを管理すると、通知を使用するより効率的で使いやすいことが分かりました
キーボードが存在するときにビューを上下に移動する
注:これは、iOSが提供する内蔵キーボードでのみ機能します
迅速:
UIViewControllerが表示されているときにフレームの起点を増やし、非表示にしたときに縮小するには、クラスに次の関数を追加します。
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()
メソッドで、次のオブザーバーを追加します:
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)
これは、キーボードのheightプロパティを使用して、任意の画面サイズで機能します。
目的 - C:
Objective-Cで同じことをするために、このコードを使うことができます:
- (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;
}];
}