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) //不編輯時按鈕不可按
}
}
