【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日でした
以上です