textView相對於textField只能輸入一行內容,超過便不能滑動的特性來講,textView彈性許多(可以滑動文字框,輸入很多字),但他的屬性基本上與textField一樣,今天就來寫個範例:

畫面上有兩個按鈕和一個textView,這次除了要做textView還要讓鍵盤彈起時自動修正textView的frame(如下圖)

程式碼如下:
先定義按鈕們和textView,加上一些等下frame要用的x,y寬高
注意!ButtonTag是要用來區別undo與done按鈕的,搭配switch使用
import UIKit class ViewController: UIViewController { var previousString = "" var undoButton: UIButton! var doneButton: UIButton! var textView: UITextView! enum ButtonTags: Int { case undo = 1, done } var frameW: CGFloat! var frameH: CGFloat! var gap: CGFloat! var textY: CGFloat! var textW: CGFloat! var textH: CGFloat! var keyboardHeight: CGFloat = 0
再來viewDidLoad我們直接呼叫self.initUI這個方法去使用
在viewWillAppear裡加上點擊textView會彈出keyboard的code
其用意在於對NotificationCenter呼叫鍵盤的開關,方法是固定的,寫久就習慣了
viewWillDisappear的部分是怕如果有不同的頁面要切換,那把通知給取消,可以減少不少記憶體的使用
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. self.initUI() } override func viewWillAppear(_ animated: Bool) { self.buttonEnable(false) // NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil) //通知鍵盤被打開 NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: .UIKeyboardWillHide, object: nil) //通知鍵盤被關閉 } override func viewWillDisappear(_ animated: Bool) { NotificationCenter.default.removeObserver(self) //取消上面通知,減少記憶體的使用 }
下面一連串的只是建構物件和給物件位置&屬性,便不再贅述
//MARK: - fcuntion private func initUI() { self.view.backgroundColor = UIColor(red: 0.95, green: 0.95, blue: 0.95, alpha: 1.0) self.frameW = UIScreen.main.bounds.width self.frameH = UIScreen.main.bounds.height self.gap = 10 let buttonW = (self.frameW - self.gap * 3) / 2 let buttonH: CGFloat = 30 self.undoButton = UIButton(frame: CGRect(x: self.gap, y: 20, width: buttonW, height: buttonH)) self.undoButton.setTitle("UNDO", for: .normal) self.undoButton.setTitleColor(UIColor.darkGray, for: .normal) self.undoButton.addTarget(self, action: #selector(self.onButtonAction(_:)), for: .touchUpInside) self.undoButton.tag = ButtonTags.undo.rawValue self.undoButton.layer.cornerRadius = 8.0 self.undoButton.layer.borderWidth = 1.0 self.undoButton.layer.borderColor = UIColor.darkGray.cgColor self.view.addSubview(self.undoButton) let undoX = self.gap + buttonW + self.gap self.doneButton = UIButton(frame: CGRect(x: undoX, y: 20, width: buttonW, height: buttonH)) self.doneButton.setTitle("DONE", for: .normal) self.doneButton.setTitleColor(UIColor.darkGray, for: .normal) self.doneButton.addTarget(self, action: #selector(self.onButtonAction(_:)), for: .touchUpInside) self.doneButton.tag = ButtonTags.done.rawValue self.doneButton.layer.cornerRadius = 8.0 self.doneButton.layer.borderWidth = 1.0 self.doneButton.layer.borderColor = UIColor.darkGray.cgColor self.view.addSubview(self.doneButton) self.textY = 20 + buttonH + self.gap self.textW = self.frameW - gap * 2 self.textH = frameH - textY! - gap //加!可能是nil,解決nil不能減nil的問題 self.textView = UITextView(frame: CGRect(x: gap, y: textY, width: textW, height: textH)) self.textView.layer.cornerRadius = 8.0 self.textView.layer.borderWidth = 1.0 self.textView.layer.borderColor = UIColor.darkGray.cgColor self.textView.textColor = UIColor.darkGray self.view.addSubview(textView) self.textView.delegate = self self.textView.text = """ 如果各位有在關注本網站應該會發從昨天開始就連不上了,找問題找了一天,才發現原先買的Cooler Master 酷媽GX 450W整個不讓我開機了….無奈之下拿其他的Power來支援,更無奈的事發生了…..有7顆硬碟SATA電源線卻不夠,果斷拿起之前主任給的雙Power電源線,加上大4pin轉SATA店的線兩條,伺服器火速重新上線!! """ }
按鈕的方法寫在這裡,比較主要是setUI要計算當鍵盤彈起textView的高度那段
onButtonAction定義當兩顆按鈕按下要做什麼
keyboardWillShow那段比較複雜,講述keyboard彈起的動畫,也是固定寫法
keyboardWillHide比較簡單,只是回傳歸零而已
func buttonEnable(_ enable:Bool){ self.undoButton.isEnabled = enable //上一步的按鈕是否被觸發 } func setUI(isKeyboardShown: Bool) { let offset = isKeyboardShown ? self.keyboardHeight : 0 //鍵盤有出來就是鍵盤的高度,沒出來就是0 self.textView.frame.size.height = self.textH - offset } //MARK: - selector func onButtonAction(_ sender: UIButton) { guard let tag = ButtonTags(rawValue: sender.tag) else { return } switch tag { case .undo: self.textView.text = self.previousString //上一步 case .done: self.view.endEditing(true) //End Editing } } func keyboardWillShow(notification: NSNotification) { if let infoKey = notification.userInfo?[UIKeyboardFrameEndUserInfoKey], let rectValue = (infoKey as AnyObject).cgRectValue { let keyboardFrame = self.view.convert(rectValue, to: nil) self.keyboardHeight = keyboardFrame.height self.setUI(isKeyboardShown: true) } // } func keyboardWillHide(notification: NSNotification) { self.setUI(isKeyboardShown: false) } }
最後收尾是用extension把UITextView的Delegate寫在這兒
為暫存文字的方法:
textViewDidBeginEditing開始編輯就儲存一次,但還不夠
所以在用shouldChangeTextIn的方式存一遍
extension ViewController: UITextViewDelegate { func textViewDidBeginEditing(_ textView: UITextView) { self.previousString = self.textView.text //暫存輸入的文字 } func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { print(text) self.previousString = self.textView.text self.buttonEnable(true) //編輯時按鈕可按 return true } func textViewDidEndEditing(_ textView: UITextView) { self.buttonEnable(false) //不編輯時按鈕不可按 } }