【swift】複数のUITextField、UITextViewがキーボードで隠れないにする1番簡単な方法
はじめに
まぁ、1番簡単というのは個人的にはなのですが、、、
以前にもこちらで同じような内容を書いたんですけど
今回のやり方のほうが個人的には汎用的であって、今後、このやり方でやるだろうなとういことでメモしておく
実装
今回は、1画面にUITextFieldとUITextViewが複数ある場合を想定します。
ポイントとしては、scrolle.ContentOffsetとView(UITextFieldまたはUITextViewのframe.y)を合わせることかと思う。
で、その際に画面をせりあがらせる前の位置を記録しておいて、キーボードが閉じるときにそこに戻すということでしょうか
class ViewController: UITextFieldDelegate, UITextViewDelegate {
・・・
// フォーカスが当たっているものをここにセットする(両方セットされることはないようにする)
var activeTextField: UITextField?
var activeTextView: UITextView?
// キーボードを開く前の表示位置を保持する
var saveContentOffsetY: CGFloat?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
・・・
}
// UITeixtFieldが選択された場合
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
self.activeTextField = textField
self.activeTextView = nil
return true
}
// UITextViewが選択された場合
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
self.activeTextView = textView
self.activeTextField = nil
return true
}
// キーボードが表示されるときに通知される
// 表示位置を選択されたViewの位置にくるようにセットする
func handleKeyboardWillShowNotification(notification: NSNotification) {
// UITextFieldが選択され場合
if activeTextField != nil {
self.saveContentOffsetY = self.scrollView.contentOffset.y
self.scrollView.contentOffset.y = activeTextField!.frame.origin.y - 50
// UITextViewが選択された場合
} else if activeTextView != nil {
self.saveContentOffsetY = self.scrollView.contentOffset.y
self.scrollView.contentOffset.y = activeTextView!.frame.origin.y - 50
}
}
// キーボードが隠れるとき、元の位置に戻す
func handleKeyboardWillHideNotification(notification: NSNotification) {
self.scrollView.contentOffset.y = self.saveContentOffsetY!
}
・・・
}個人的にはこのやり方が1番直感的でわかりやすかったです
サンプルコード
※完成例

import UIKit
class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate, CustomTextViewDelegate {
var scrollView = UIScrollView()
var activeTextField: UITextField?
var activeTextView: UITextView?
var saveContentOffsetY: CGFloat?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.scrollView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)
let marginLeft: CGFloat = 30
let labelHeight: CGFloat = 30
let inputHeight: CGFloat = 50
let labelEmail = CustomLabel(frame: CGRectMake(marginLeft, 50, self.view.frame.size.width - (marginLeft*2), labelHeight))
labelEmail.text = "メールアドレス"
self.scrollView.addSubview(labelEmail)
let inputEmail = CustomTextfield(frame: CGRectMake(marginLeft, labelEmail.frame.origin.y + labelEmail.frame.size.height + 5, self.view.frame.size.width - (marginLeft*2), inputHeight))
inputEmail.delegate = self
self.scrollView.addSubview(inputEmail)
let labelName = CustomLabel(frame: CGRectMake(marginLeft, inputEmail.frame.origin.y + inputEmail.frame.size.height + 10, self.view.frame.size.width - (marginLeft*2), labelHeight))
labelName.text = "名前"
self.scrollView.addSubview(labelName)
let inputName = CustomTextfield(frame: CGRectMake(marginLeft, labelName.frame.origin.y + labelName.frame.size.height + 5, self.view.frame.size.width - (marginLeft*2), inputHeight))
inputName.delegate = self
self.scrollView.addSubview(inputName)
let labelAddress = CustomLabel(frame: CGRectMake(marginLeft, inputName.frame.origin.y + inputName.frame.size.height + 10, self.view.frame.size.width - (marginLeft*2), labelHeight))
labelAddress.text = "住所"
self.scrollView.addSubview(labelAddress)
let inputAddress = CustomTextfield(frame: CGRectMake(marginLeft, labelAddress.frame.origin.y + labelAddress.frame.size.height + 5, self.view.frame.size.width - (marginLeft*2), inputHeight))
inputAddress.delegate = self
self.scrollView.addSubview(inputAddress)
let labelDescription = CustomLabel(frame: CGRectMake(marginLeft, inputAddress.frame.origin.y + inputAddress.frame.size.height + 10, self.view.frame.size.width - (marginLeft*2), labelHeight))
labelDescription.text = "紹介文"
self.scrollView.addSubview(labelDescription)
let inputDescription = CustomTextView(frame: CGRectMake(marginLeft, labelDescription.frame.origin.y + labelDescription.frame.size.height + 5, self.view.frame.size.width - (marginLeft*2), 150))
inputDescription.delegate = self
inputDescription._delegate = self
self.scrollView.addSubview(inputDescription)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, inputDescription.frame.origin.y + inputDescription.frame.size.height + 50)
self.view.addSubview(self.scrollView)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: "handleKeyboardWillShowNotification:", name: UIKeyboardWillShowNotification, object: nil)
notificationCenter.addObserver(self, selector: "handleKeyboardWillHideNotification:", name: UIKeyboardWillHideNotification, object: nil)
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
self.activeTextField = textField
self.activeTextView = nil
return true
}
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
self.activeTextView = textView
self.activeTextField = nil
return true
}
func handleKeyboardWillShowNotification(notification: NSNotification) {
if activeTextField != nil {
self.saveContentOffsetY = self.scrollView.contentOffset.y
self.scrollView.contentOffset.y = activeTextField!.frame.origin.y - 50
} else if activeTextView != nil {
self.saveContentOffsetY = self.scrollView.contentOffset.y
self.scrollView.contentOffset.y = activeTextView!.frame.origin.y - 50
}
}
func handleKeyboardWillHideNotification(notification: NSNotification) {
self.scrollView.contentOffset.y = self.saveContentOffsetY!
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textViewShouldReturn(textView: UITextView) -> Bool {
textView.resignFirstResponder()
return true
}
func onCloseTextView() {
self.activeTextView?.resignFirstResponder()
}
}
class CustomLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
self.setUp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setUp()
}
func setUp() {
self.font = UIFont.systemFontOfSize(18)
}
}
class CustomTextfield: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
self.setUp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setUp()
}
func setUp() {
self.font = UIFont.systemFontOfSize(18)
self.layer.borderWidth = 0.5
self.layer.borderColor = UIColor.grayColor().CGColor
self.layer.cornerRadius = 3
}
}
class CustomTextView: UITextView {
var _delegate: CustomTextViewDelegate! = nil
required internal init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setUp()
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer);
self.setUp()
}
func setUp() {
self.font = UIFont.systemFontOfSize(18)
self.layer.borderWidth = 0.5
self.layer.borderColor = UIColor.grayColor().CGColor
self.layer.cornerRadius = 3
let accessoryView = UIView(frame: CGRectMake(0, 0, self.frame.size.width, 44))
accessoryView.backgroundColor = UIColor.whiteColor()
let closeButton = UIButton(frame: CGRectMake(self.frame.size.width - 50, 5, 100, 30))
closeButton.setTitle("完了", forState: UIControlState.Normal)
closeButton.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
closeButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Right
closeButton.addTarget(self, action: "onClose:", forControlEvents: .TouchUpInside)
accessoryView.addSubview(closeButton)
self.inputAccessoryView = accessoryView
}
func onClose(sender: UIButton) {
self._delegate.onCloseTextView()
}
}
protocol CustomTextViewDelegate {
func onCloseTextView()->Void
}とはいえ、やっぱりアプリ開発はいちいち考慮することが多くて難しいと思った今日1日でした
以上です