swift - 自定义表情键盘 + 如何和后台交互

自定义表情视图(collectionView+自定义UICollectionViewFlowLayout)

image.png

HQEmjioView是定义的表情视图类,HQEmjioCell是collectionview的cell,HQEmjioTool是一些工具类的封装。

首先我们自定义一个表情键盘的思路就是
1、先把表情键盘页面写好,包括一些代理事件,表情布局等。
2、视图写好以后会去思考事件。包括表情点击如何展示在textView上,表情的添加和删除,点击发送如何把数据传给后台,以及如何将后台发来的数据还原。这里我们用到了一个很重要的属性NSAttributedString,通过富文本去实现这一系列的操作。

具体代码思路
1、懒加载表情视图

 private lazy var emjioView:HQEmjioView? = HQEmjioView.emjioView()

2、监听键盘

func addKeyBoardNotification() {
        let notif = NotificationCenter.default
        notif.addObserver(self,
                          selector: #selector(keyboardWillChangeFrame),
                          name: .UIKeyboardWillChangeFrame,
                          object: nil)
    }

3、键盘响应时做出相应的操作

    /// 键盘响应通知
    @objc func keyboardWillChangeFrame(_ note: Notification) {
     
        guard let kbInfo = note.userInfo,
        let duration = kbInfo[UIKeyboardAnimationDurationUserInfoKey] as? TimeInterval,
        let kbFrameInfo = kbInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else {
            return
        }
        let kbFrame = kbFrameInfo.cgRectValue
        let kbInset = UIScreen.main.bounds.size.height - (kbFrame.minY) // 键盘偏移量
        let animationCurve = kbInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt

        if kbInset > 0.0 {
            UIView.animate(withDuration: duration, delay: 0.0,
                           options: UIViewAnimationOptions(rawValue: animationCurve),
                           animations: { () -> Void in
                            
                            self.viewBottomConstrains.constant = -(kbInset)
                            self.view.layoutIfNeeded()
                            
            }, completion: {(finished) -> () in
            })
        } else {
            self.viewBottomConstrains.constant = self.bottomLayoutGuide.length
        }
        
    }

4、表情按钮点击时切换键盘

@IBAction func emjioBtnClick(_ sender: UIButton) {
        textView.resignFirstResponder()
        sender.isSelected = !sender.isSelected
        if sender.isSelected {
            textView.inputView = self.emjioView
        }else {
            textView.inputView = nil
        }
        textView.becomeFirstResponder()
    }

5、点击按钮让表情显示在textview上

    func emjioSelect(attachment: NSAttributedString) {
        /// 在插入表情时先判断是否有选择的字符,如果有选择则先删除选择的字符再进行插入操作
        if textView.selectedRange.length > 0 {
            textView.textStorage.deleteCharacters(in: textView.selectedRange)
            textView.selectedRange = NSMakeRange(textView.selectedRange.location, 0)
        }
        textView.textStorage.insert(attachment, at: textView.selectedRange.location)
        textView.selectedRange = NSMakeRange(textView.selectedRange.location + 1, textView.selectedRange.length)
        textViewDidChange(textView)
    }
// 将表情图片转化为可以显示的富文本 - HQEmjioTool.swift
func getAttachment(imageName:String,dic:(String,String)) -> NSAttributedString {
        let attachment = HQTextAttachment()
        attachment.text = dic.0
        attachment.image = UIImage(named: imageName)
        attachment.bounds = CGRect(x: 0, y: -4, width: 16, height: 16)
        let attributedStr = NSAttributedString(attachment: attachment)
        let muAtt = NSMutableAttributedString(attributedString: attributedStr)
        muAtt.addAttribute(.font, value: UIFont.systemFont(ofSize: 14), range: NSRange(location: 0, length: muAtt.length))
        return muAtt
    }

6、删除表情、和TextView自适应大小

func emjioDelete() {
        if textView.selectedRange.length > 0 {
            let range = textView.selectedRange
            textView.textStorage.deleteCharacters(in: range)
            textView.selectedRange = NSMakeRange(range.location, 0)
        } else if textView.selectedRange.location > 0 {
            let range = NSMakeRange(textView.selectedRange.location - 1, 1)
            textView.textStorage.deleteCharacters(in: range)
            textView.selectedRange = NSMakeRange(range.location, 0)
        }
        textViewDidChange(textView)
    }
    // textView自适应大小
    func textViewDidChange(_ textView: UITextView) {
        var bounds = textView.bounds
        let maxSize = CGSize(width: bounds.size.width, height: CGFloat.greatestFiniteMagnitude)
        let newSize = textView.sizeThatFits(maxSize)
        bounds.size.height = newSize.height
        if bounds.size.height < MAX_HEIGHT {
            viewHeightConstrains.constant = bounds.size.height > 36 ? bounds.size.height : 36
            textView.isScrollEnabled = false
        }else {
            viewHeightConstrains.constant = MAX_HEIGHT
            textView.isScrollEnabled = true
        }
    }

7、如何将系统键盘改为发送,并监听发送事件

 textView.returnKeyType = .send
 func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if text == "\n" {
            sendBtnClick()
            return false
        }
        return true
  }

8、如何将TextView中的属性文字以字符串的形式发送给后台(后台是不支持直接属性文字的)- HQEmjioTool.swift

// 将表情文字转化成后台能接收的文字
    func plainText() -> (String) {
        let plainStr = NSMutableString(string: string)
        var base = 0
        enumerateAttribute(NSAttributedStringKey.attachment,
                           in: NSMakeRange(0, length),
                           options: NSAttributedString.EnumerationOptions(rawValue: 0)) { (value, range, stop) -> Void in
                            if let attachment = value as? HQTextAttachment {
                                plainStr.replaceCharacters(in: NSMakeRange(range.location + base, range.length), with: attachment.text)
                                base += attachment.text.characters.count - 1
                            }
        }
        return (plainStr as String)
    }

9、如何将后台发送回来的数据转化成属性文字再显示出来

// 将后台发过来的文字转化成能显示给用户的文字
     func attributedText(withChatMsg content: String) -> NSMutableAttributedString {
        
        let attrContent = NSMutableAttributedString(string: content)
        do {
            let regex = try NSRegularExpression(pattern: regulaPattern, options: .caseInsensitive)
            let allMatches = regex.matches(in: content,
                                           options: NSRegularExpression.MatchingOptions(rawValue: 0),
                                           range: NSMakeRange(0, attrContent.length))
            let resultAttrString = NSMutableAttributedString()
            var range = NSMakeRange(0, 0)
            for match in allMatches {
                var emotionPath = ""
                
                if match.range.location != range.location {
                    range.length = match.range.location - range.location
                    resultAttrString.append(attrContent.attributedSubstring(from: range))
                }
                range.location = match.range.location + match.range.length
                if match.range.length > 0 {
                    let emot = attrContent.attributedSubstring(from: match.range)
                    for emotDic in emotions() {
                        if emotDic.0 == emot.string {
                            emotionPath = emotPath + emotDic.1
                            resultAttrString.append(getAttachment(imageName: emotionPath, dic: emotDic))
                            break
                        }
                    }
                    if emotionPath == "" {
                        resultAttrString.append(emot) // 找不到对应图像名称就直接加上去
                    }
                }
            }
            if range.location != attrContent.length {
                range.length = attrContent.length - range.location
                resultAttrString.append(attrContent.attributedSubstring(from: range))
            }
            return resultAttrString
        } catch let error {
            print(error)
        }
        return attrContent
    }
}
变成属性文字的过程。。。

具体如何自定义collectionview的表情键盘可以看我项目里面的代码。GitHub

你可能感兴趣的:(swift - 自定义表情键盘 + 如何和后台交互)