Swift日誌:textView使用技巧

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

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

This site uses Akismet to reduce spam. Learn how your comment data is processed.