| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361 |
- //
- // KeyboardViewController.swift
- // AiKeyboard
- //
- // Created by Destiny on 2025/4/23.
- //
- import UIKit
- import Lottie
- import SnapKit
- import MarqueeLabel
- import GravityEngineSDK
- enum KeyboardType: Int {
- case help = 0 // 帮聊
- case teach = 1 // 教你说
- case prologue = 2 // 开场白
- }
- class KeyboardViewController: UIInputViewController {
-
- struct UX {
- static let keyboardDefaultHeight = 292.0
- static let keyboardWithTipsHeight = 318.0
- }
-
- // 选择的键盘类型
- var chooseType: KeyboardType = .help {
- didSet {
- UserDefaults.standard.setValue(chooseType.rawValue, forKey: "KeyboardChooseType")
- self.updateMainViewUI()
- }
- }
- // 键盘列表
- var keyboardList: [KeyboardModel]?
- // 选择的键盘
- var chooseKeyboard: KeyboardModel?
- // 人设列表
- var characterList: [CharacterModel]?
- // 开场白列表
- var prologueList: [PrologueModel]?
-
- // 开始持续删除
- private var deleteTimer: Timer?
- private var deleteAcceleration: TimeInterval = 0.1
-
- var heightConstriaint: NSLayoutConstraint?
-
- lazy var bgImageView: UIImageView = {
-
- let imageView = UIImageView()
- imageView.image = UIImage(named: "keyboard_bg")
- return imageView
- }()
-
- lazy var menuBtn: UIButton = {
-
- let button = UIButton()
- button.setImage(UIImage(named: "keyboard_menu_btn"), for: .normal)
- button.addTarget(self, action: #selector(menuBtnClickAction), for: .touchUpInside)
- return button
- }()
-
- lazy var heartAnimation: LottieAnimationView = {
-
- let animate = LottieAnimationView(name: "heart")
- animate.loopMode = .loop
- animate.backgroundColor = .clear
-
- animate.isUserInteractionEnabled = true
- let tap = UITapGestureRecognizer(target: self, action: #selector(heartBtnClickAction))
- animate.addGestureRecognizer(tap)
-
- return animate
- }()
-
- lazy var heartLabel: UILabel = {
-
- let label = UILabel()
- label.text = "30%"
- label.font = .boldSystemFont(ofSize: 12)
- label.textColor = .white
-
- label.isUserInteractionEnabled = true
- let tap = UITapGestureRecognizer(target: self, action: #selector(heartBtnClickAction))
- label.addGestureRecognizer(tap)
-
- return label
- }()
-
- lazy var exchangeBtn: UIButton = {
-
- let button = UIButton()
- button.setImage(UIImage(named: "keyboard_exchange_btn"), for: .normal)
- button.addTarget(self, action: #selector(exchangeBtnClickAction), for: .touchUpInside)
- return button
- }()
-
- lazy var chooseView: UIView = {
-
- let view = UIView()
- view.backgroundColor = .white
- view.layer.cornerRadius = 17
- return view
- }()
-
- lazy var functionView: UIView = {
-
- let view = UIView()
- view.backgroundColor = .hexStringColor(hexString: "#DDCFFD")
- view.layer.cornerRadius = 16
-
- view.isUserInteractionEnabled = true
- let tap = UITapGestureRecognizer(target: self, action: #selector(functionBtnClickAction))
- view.addGestureRecognizer(tap)
- return view
- }()
-
- lazy var functionLabel: UILabel = {
-
- let label = UILabel()
- label.text = "帮聊"
- label.font = .boldSystemFont(ofSize: 14)
- label.textColor = .hexStringColor(hexString: "#000000", alpha: 0.8)
- return label
- }()
-
- lazy var arrowIcon: UIImageView = {
-
- let imageView = UIImageView()
- imageView.image = UIImage(named: "icon_arrow_down")
- return imageView
- }()
-
- lazy var userChangeBtn: UIButton = {
-
- let btn = UIButton()
- btn.setTitle("小蕾", for: .normal)
- btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
- btn.setTitleColor(.hexStringColor(hexString: "#000000").withAlphaComponent(0.8), for: .normal)
- btn.setImage(UIImage(named: "keyboard_user_change_icon"), for: .normal)
- btn.setImageTitleLayout(.imgRight, spacing: 8)
- btn.addTarget(self, action: #selector(changeBtnClickAction), for: .touchUpInside)
- return btn
- }()
-
- lazy var userChangeView: UIView = {
-
- let view = UIView()
- let tap = UITapGestureRecognizer(target: self, action: #selector(changeBtnClickAction))
- view.addGestureRecognizer(tap)
- return view
- }()
-
- lazy var userChangeLabel: MarqueeLabel = {
-
- let label = MarqueeLabel(frame: .zero, duration: 3.0, fadeLength: 0)
- label.type = .leftRight
- label.text = "通用键盘"
- label.font = .systemFont(ofSize: 14)
- label.textColor = .hexStringColor(hexString: "#000000")
- label.textAlignment = .center
- return label
- }()
-
- lazy var userChangeIcon: UIImageView = {
-
- let icon = UIImageView()
- icon.image = UIImage(named: "keyboard_user_change_icon")
- return icon
- }()
-
- lazy var helpView: KeyboardHelpView = {
-
- let view = KeyboardHelpView()
- view.delegate = self
- view.helpDelegate = self
- return view
- }()
-
- lazy var teachView: KeyboardTeachView = {
-
- let view = KeyboardTeachView()
- view.delegate = self
- view.teachDelegate = self
- view.isHidden = true
- return view
- }()
-
- lazy var prologueView: KeyboardPrologueView = {
-
- let view = KeyboardPrologueView()
- view.delegate = self
- view.prologueDelegate = self
- view.isHidden = true
- return view
- }()
-
- lazy var exchangeView: KeyboardExchangeView = {
-
- let view = KeyboardExchangeView()
- view.isHidden = true
- view.delegate = self
- return view
- }()
-
- lazy var menuView: KeyboardMenuView = {
-
- let view = KeyboardMenuView()
- view.isHidden = true
- view.delegate = self
- return view
- }()
-
- lazy var loginView: KeyboardLoginTipView = {
-
- let view = KeyboardLoginTipView()
- view.isHidden = true
- view.loginBtnClosure = {
- self.navigateToLogin()
- }
- view.backBtnClosure = {
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.login_close)
- self.clearPopView()
- self.loginView.isHidden = true
- self.nowShowView?.isHidden = false
- }
- return view
- }()
-
- lazy var vipView: KeyboardVipTipView = {
-
- let view = KeyboardVipTipView()
- view.isHidden = true
- view.unlockBtnClosure = {
- self.navigateToMembership()
- }
- view.backBtnClosure = {
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.vip_close)
- self.clearPopView()
- self.vipView.isHidden = true
- self.nowShowView?.isHidden = false
- }
- return view
- }()
-
- lazy var tipPopView: KeyboardTipsPopView = {
-
- let view = KeyboardTipsPopView()
- view.isHidden = true
- view.settingBtnClosure = {
-
- if let url = URL(string: "com.qihuan.zhuiaijianpan:///open/setting") {
- self.openURL(url)
- }
- }
- view.backBtnClosure = {
-
- self.tipPopView.isHidden = true
- }
- return view
- }()
-
- var nowShowView: UIView?
-
- // 命令检查定时器
- private var guideCheckTimer: Timer?
-
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- self.prepareHeightConstraint()
- }
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- setUI()
- checkFullAccess()
-
- KeyboardApi.initParams()
-
- let isFullAccess = self.hasFullAccess
-
- if isFullAccess {
- requestKeyboardList()
- requestPrologueList()
- } else {
- getLocalSystemKeyboard()
- }
-
- if let defaultType = UserDefaults.standard.value(forKey: "KeyboardChooseType") as? Int, let type = KeyboardType(rawValue: defaultType) {
- self.chooseType = type
- }
-
-
- startMonitoringPasteboard()
- startListenGuide()
- // self.nextKeyboardButton.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
- // self.nextKeyboardButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
- }
-
- override func viewWillDisappear(_ animated: Bool) {
-
- super.viewWillDisappear(animated)
- }
-
- override func viewWillLayoutSubviews() {
- super.viewWillLayoutSubviews()
- }
-
- override func updateViewConstraints() {
- super.updateViewConstraints()
-
- if self.view.frame.size.width == 0 && self.view.frame.size.height == 0 {
- return
- }
-
- self.prepareHeightConstraint()
- }
-
- override func textWillChange(_ textInput: UITextInput?) {
- // The app is about to change the document's contents. Perform any preparation here.
- }
-
- override func textDidChange(_ textInput: UITextInput?) {
- // The app has just changed the document's contents, the document context has been updated.
-
- var textColor: UIColor
- let proxy = self.textDocumentProxy
- if proxy.keyboardAppearance == UIKeyboardAppearance.dark {
- textColor = UIColor.white
- } else {
- textColor = UIColor.black
- }
- }
- func prepareHeightConstraint() {
-
- if self.heightConstriaint == nil {
-
- if let view = self.view {
- self.heightConstriaint = NSLayoutConstraint(item: view, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 0.0, constant: UX.keyboardDefaultHeight)
- // self.heightConstriaint?.priority = UILayoutPriority(750)
- self.view.addConstraint(self.heightConstriaint!)
- }
- } else {
- self.heightConstriaint?.constant = UX.keyboardDefaultHeight
- }
- }
-
- // 开始监听是否需要打开引导页
- func startListenGuide() {
-
- let isNotFirstOpen = UserDefaults.standard.bool(forKey: "isNotFirstOpenKey")
- if !isNotFirstOpen {
- // 创建定时器定期检查命令
- guideCheckTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
- self?.popGuideView()
- }
- }
- }
-
- func popGuideView() {
-
- let hasFullAccess = self.hasFullAccess
-
- let showGuide = KeyboardSharedDataManager.shared.getIsShowGuide()
- if hasFullAccess && (showGuide == true) {
-
- let guideView = KeyboardGuideView()
- self.view.addSubview(guideView)
- guideView.snp.makeConstraints { make in
- make.top.left.right.bottom.equalTo(0)
- }
- UserDefaults.standard.set(true, forKey: "isNotFirstOpenKey")
-
- guideCheckTimer?.invalidate()
- guideCheckTimer = nil
- }
- }
- }
- // MARK: - 网络请求
- extension KeyboardViewController {
-
- func getLocalSystemKeyboard() {
-
- if let systemKeyboardJson = KeyboardSharedDataManager.shared.getSystemkeyboard() {
- if let jsonData = systemKeyboardJson.data(using: .utf8) {
- let decoder = JSONDecoder()
- do {
- let characterList = try decoder.decode([CharacterModel].self, from: jsonData)
- self.characterList = characterList
- self.updateCharacterUI()
- // 成功解码,可以使用 keyboardModel
- print("CharacterList: \(characterList)")
- } catch {
- print("解码 KeyboardModel 失败: \(error)")
- }
- }
- }
- }
-
- // 获取键盘列表
- func requestKeyboardList() {
-
- KeyboardNetworkManager.request(.keyboard_list(params: [:])) { response in
-
- if let keyboardInfos = try? response.mapArray(KeyboardModel.self, atKeyPath: "data.keyboardInfos") {
- self.keyboardList = keyboardInfos
- self.updateUserChangeBtnUI()
- self.requestCharacterList()
- print("成功解析数组,数量:\(keyboardInfos.count)")
- }
- } fail: { code, error in
-
- self.view.toast(text: error)
- print(error)
- }
- }
-
- // 获取键盘人设列表
- func requestCharacterList() {
-
- var params: [String: Any] = [String: Any]()
- if let chooseKeyboard = self.chooseKeyboard {
- params = ["keyboardId": chooseKeyboard.id ?? ""]
- }
- KeyboardNetworkManager.request(.character_list(params: params)) { response in
-
- if let characterInfos = try? response.mapArray(CharacterModel.self, atKeyPath: "data.characterInfos") {
- self.characterList = characterInfos
- self.updateCharacterUI()
- print("成功解析数组,数量:\(characterInfos.count)")
- }
- } fail: { code, error in
-
- self.view.toast(text: error)
- print(error)
- }
- }
-
- // 获取键盘开场白列表
- func requestPrologueList() {
-
- KeyboardNetworkManager.request(.prologue_list(params: [:])) { response in
-
- if let prologueList = try? response.mapArray(PrologueModel.self, atKeyPath: "data.prologues") {
- self.prologueList = prologueList
- self.updatePrologueUI()
- }
- } fail: { code, error in
-
- self.view.toast(text: error)
- print(error)
- }
- }
-
- // 选择键盘
- func requestChooseKeyboard(keyboardId: String, success: @escaping (() -> Void)) {
-
- var params: [String: Any] = [String: Any]()
- params = ["keyboardId": keyboardId]
- KeyboardNetworkManager.request(.keyboard_choose(params: params)) { response in
-
- self.view.toast(text: "保存成功")
- success()
- } fail: { code, error in
-
- self.view.toast(text: error)
- print(error)
- }
- }
-
- // 请求获取超会回
- func requestSuperReply(characterId: String, content: String, complete: @escaping ((String) -> ())) {
-
- var params: [String: Any] = [String: Any]()
- params = ["keyboardId": self.chooseKeyboard?.id ?? "",
- "characterId": characterId,
- "content": content]
- KeyboardNetworkManager.request(.chat_super_reply(params: params)) { response in
-
- if let contentModel = try? response.mapObject(ReplyModel.self, atKeyPath: "data") {
- complete(contentModel.content ?? "")
- } else {
- complete("")
- }
- } fail: { code, error in
-
- self.view.toast(text: error)
- print(error)
- complete("")
-
- if code == 1006 {
- // 弹出登录页
- self.popLoginView()
- } else if code == 1008 {
- // 弹出会员页
- self.popVipView()
- }
- }
- }
-
- // 请求获取超会说
- func requestSuperSpeak(characterId: String, content: String, complete: @escaping (([String]) -> ())) {
-
- var params: [String: Any] = [String: Any]()
- params = ["keyboardId": self.chooseKeyboard?.id ?? "",
- "characterId": characterId,
- "content": content]
- KeyboardNetworkManager.request(.chat_super_speak(params: params)) { response in
-
- if let contentModel = try? response.mapObject(SpeakModel.self, atKeyPath: "data"), let list = contentModel.list {
- complete(list)
- } else {
- complete([String]())
- }
- } fail: { code, error in
-
- self.view.toast(text: error)
- print(error)
- complete([String]())
-
- if code == 1006 {
- // 弹出登录页
- self.popLoginView()
- } else if code == 1008 {
- // 弹出会员页
- self.popVipView()
- }
- }
- }
-
- // 请求获取超会说
- func requestSuperPrologue(name: String, complete: @escaping (([String]) -> ())) {
-
- var params: [String: Any] = [String: Any]()
- params = ["name": name]
- KeyboardNetworkManager.request(.chat_super_prologue(params: params)) { response in
-
- if let contentModel = try? response.mapObject(SpeakModel.self, atKeyPath: "data"), let list = contentModel.list {
- complete(list)
- } else {
- complete([String]())
- }
- } fail: { code, error in
-
- self.view.toast(text: error)
- print(error)
- complete([String]())
-
- if code == 1006 {
- // 弹出登录页
- self.popLoginView()
- } else if code == 1008 {
- // 弹出会员页
- self.popVipView()
- }
- }
- }
- }
- // MARK: - 点击事件
- extension KeyboardViewController: KeyboardMenuViewDelegate, KeyboardExchangeViewDelegate {
-
- // 亲密度按钮点击
- @objc func heartBtnClickAction() {
-
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.initmacy_click)
- let url = URL(string: "com.qihuan.zhuiaijianpan:///intimacy")!
- openURL(url)
- }
-
- // 切换系统键盘
- @objc func exchangeBtnClickAction() {
-
- self.advanceToNextInputMode()
- }
-
- // 功能按钮选择点击
- @objc func functionBtnClickAction() {
-
- let isFullAccess = self.hasFullAccess
- if !isFullAccess {
- self.popTipsView()
- return
- }
-
- KeyboardFunctionPopView.show(view: self.view, selectType: self.chooseType) { type in
- self.chooseType = type
- switch type {
- case .help:
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.mode_help)
- case .teach:
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.mode_teach)
- case .prologue:
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.mode_prologue)
- }
- }
- }
-
- // 菜单按钮点击
- @objc func menuBtnClickAction() {
-
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.menu_click)
- clearPopView()
- self.menuView.isHidden = false
- }
-
- // 菜单按钮返回
- func menuBackBtnClickAction() {
-
- clearPopView()
- self.nowShowView?.isHidden = false
- }
-
- // 会员按钮点击
- func vipBtnClickAction() {
-
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.menu_vip)
- navigateToMembership()
- }
-
- // 定制人设
- func diyBtnClickAction() {
-
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.menu_diy)
- let url = URL(string: "com.qihuan.zhuiaijianpan:///character/custom")!
- openURL(url)
- }
-
- // 人设市场
- func marketBtnClickAction() {
-
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.menu_market)
- let url = URL(string: "com.qihuan.zhuiaijianpan:///character/market")!
- openURL(url)
- }
-
- // 跳转到主应用的登录页面
- @objc func navigateToLogin() {
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.login_click)
- let url = URL(string: "com.qihuan.zhuiaijianpan:///login")!
- openURL(url)
- }
- // 跳转到主应用的会员页面
- @objc func navigateToMembership() {
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.vip_click)
- let url = URL(string: "com.qihuan.zhuiaijianpan:///member")!
- openURL(url)
- }
-
- @objc @discardableResult private func openURL(_ url: URL) -> Bool {
- var responder: UIResponder? = self
- while responder != nil {
- if let application = responder as? UIApplication {
- if #available(iOS 18.0, *) {
- application.open(url, options: [:], completionHandler: nil)
- return true
- } else {
- return application.perform(#selector(openURL(_:)), with: url) != nil
- }
- }
- responder = responder?.next
- }
- return false
- }
- // // 通用的打开URL方法
- // private func openURL(_ url: URL) {
- //// var responder = self as UIResponder?
- //// while (responder != nil && !(responder is UIApplication)) {
- //// responder = responder?.next
- //// }
- //// if responder != nil {
- //// let selectorOpenURL = sel_registerName("openURL:options:completionHandler:")
- //// if responder!.responds(to: selectorOpenURL) {
- //// self.dismissKeyboard()
- //// responder?.perform(selectorOpenURL, with: [url, nil, nil])
- //// }
- //// }
- //
- // var responder: UIResponder? = self
- // while responder != nil {
- // if let application = responder as? UIApplication {
- // if #available(iOS 18.0, *) {
- // application.open(url, options: [:], completionHandler: nil)
- // } else {
- // application.perform(#selector(openURL(_:)), with: url)
- // }
- // }
- // responder = responder?.next
- // }
- // // 不知道为什么无法使用
- //// self.extensionContext?.open(url, completionHandler: nil)
- // }
-
- // 选择键盘按钮点击
- @objc func changeBtnClickAction() {
-
- let isFullAccess = self.hasFullAccess
- if !isFullAccess {
- self.popTipsView()
- return
- }
-
- clearPopView()
- self.exchangeView.keyboardList = self.keyboardList
- self.exchangeView.isHidden = false
- }
-
- // 选择键盘后返回
- func exchangeViewBackClickAction() {
-
- clearPopView()
- self.nowShowView?.isHidden = false
- }
-
- // 点击保存按钮
- func exchangeViewSaveClickAction(keyboardList: [KeyboardModel], success: @escaping (() -> ())) {
-
- var keyboardId = ""
- var selectKeyboard: KeyboardModel?
- for keyboard in keyboardList {
- if keyboard.isChoose == true {
- keyboardId = keyboard.id ?? ""
- selectKeyboard = keyboard
- break
- }
- }
-
- if let selectKeyboard = selectKeyboard {
- // 当为系统键盘时不需要调接口
- if selectKeyboard.type == "system" {
-
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.keyboard_system)
- self.keyboardList = keyboardList
- self.chooseKeyboard = selectKeyboard
- self.requestCharacterList()
- success()
- self.userChangeLabel.text = self.chooseKeyboard?.name
- self.clearPopView()
- self.nowShowView?.isHidden = false
- } else {
-
- requestChooseKeyboard(keyboardId: keyboardId) {
-
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.keyborad_custom)
- self.keyboardList = keyboardList
- self.chooseKeyboard = selectKeyboard
- self.requestCharacterList()
- success()
- self.userChangeLabel.text = self.chooseKeyboard?.name
- self.clearPopView()
- self.nowShowView?.isHidden = false
- }
- }
- }
- }
-
- }
- extension KeyboardViewController: KeyboardHelpViewDelegate, KeyboardTeachViewDelegate, KeyboardPrologueViewDelegate {
- // 帮聊点击cell
- func helpCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping ((Bool) -> ())) {
-
- let isFullAccess = self.hasFullAccess
- if !isFullAccess {
- self.popTipsView()
- complete(false)
- return
- }
-
- // 判断是否登录
- let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
- if !isLogin {
- popLoginView()
- complete(false)
- return
- }
-
- self.requestSuperReply(characterId: characterId, content: content) { text in
- self.insertText(text)
- complete(true)
- }
- }
-
- // 教你说点击cell
- func teachCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (([String], Bool, Bool) -> ())) {
-
- let isFullAccess = self.hasFullAccess
- if !isFullAccess {
- self.popTipsView()
- complete([String](), false, false)
- return
- }
-
- // 判断是否登录
- let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
- if !isLogin {
- popLoginView()
- complete([String](), false, false)
- return
- } else {
- complete([String](), true, false)
- }
- self.requestSuperSpeak(characterId: characterId, content: content) { list in
- complete(list, true, true)
- }
- }
-
- // 教你说点击 TableViewCell
- func teachTableViewDidSelectItem(content: String) {
-
- self.insertText(content)
- }
-
- // 开场白点击cell
- func prologueCollectionViewDidSelectItem(name: String, complete: @escaping (([String], Bool, Bool) -> ())) {
-
- // 判断是否登录
- let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
- if !isLogin {
- popLoginView()
- complete([String](), false, false)
- return
- } else {
- complete([String](), true, false)
- }
-
- self.requestSuperPrologue(name: name) { list in
- complete(list, true, true)
- }
- }
-
- // 开场白点击 TableViewCell
- func prologueTableViewDidSelectItem(content: String) {
-
- self.insertText(content)
- }
- // 点击添加人设
- func addCharacterJump() {
-
- self.marketBtnClickAction()
- }
- }
- // MARK: - 处理键盘操作
- extension KeyboardViewController: KeyboardBaseViewDelegate {
-
- // 插入文字
- func insertText(_ text: String) {
-
- self.textDocumentProxy.insertText(text)
- }
- // 点击粘贴按钮
- func pasteBtnClickAction() {
-
- detectPasteboardPermissionAlert { isShowing in
- if isShowing {
- print("系统正在显示剪贴板权限弹窗")
- self.view.toast(text: "请允许访问剪贴板")
- self.openPasteTipView()
- }
- if let content = self.getPasteboardContent() {
- print("获取到剪贴板内容: \(content)")
- self.view.toast(text: "获取到剪贴板内容")
- self.helpView.setPasteStr(content: content)
- self.teachView.setPasteStr(content: content)
- } else {
- print("无法获取剪贴板内容")
- self.view.toast(text: "无法获取剪贴板内容")
- }
- }
- }
-
- // 删除按钮
- func deleteBtnClickAction() {
-
- self.textDocumentProxy.deleteBackward()
- }
-
- // 清除按钮
- func clearBtnClickAction() {
-
- // 获取当前文本
- let proxy = self.textDocumentProxy
-
- // 清空所有文本
- for _ in 0..<100 {
- proxy.deleteBackward()
- }
- }
-
- // 发送按钮
- func sendBtnClickAction() {
-
- self.textDocumentProxy.insertText("\n")
- }
-
- func deleteBtnLongPressBegin() {
-
- // 停止已有的定时器
- stopContinuousDelete()
-
- // 创建新的定时器,初始间隔为0.5秒
- deleteTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(continuousDeleteAction), userInfo: nil, repeats: false)
- }
-
- func deleteBtnLongPressEnd() {
- stopContinuousDelete()
- }
-
- // 停止删除
- func stopContinuousDelete() {
- deleteTimer?.invalidate()
- deleteTimer = nil
- deleteAcceleration = 0.1 // 重置加速度
- }
-
- @objc func continuousDeleteAction() {
- // 执行删除操作
- self.textDocumentProxy.deleteBackward()
-
- // 停止当前定时器
- deleteTimer?.invalidate()
-
- // 加速删除(最快0.05秒一次)
- let newInterval = max(0.05, 0.5 - deleteAcceleration)
- deleteAcceleration += 0.05 // 每次加速0.05秒
-
- // 创建新的更快的定时器
- deleteTimer = Timer.scheduledTimer(timeInterval: newInterval, target: self, selector: #selector(continuousDeleteAction), userInfo: nil, repeats: false)
- }
-
- // 监听剪贴板变化
- func startMonitoringPasteboard() {
-
- // 记录当前剪贴板变化计数
- var initialChangeCount = UIPasteboard.general.changeCount
- // 记录上一次的剪贴板内容
- var lastPasteboardContent: String? = UIPasteboard.general.string
-
- // 创建定时器定期检查剪贴板变化
- Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] timer in
- guard let self = self else {
- timer.invalidate()
- return
- }
-
- let currentChangeCount = UIPasteboard.general.changeCount
- if currentChangeCount != initialChangeCount {
-
- // 记录访问前的时间
- let startTime = Date()
- // 剪贴板计数已变化,检查内容是否真的变化
- if let copiedString = UIPasteboard.general.string {
- // 只有当内容真的不同时才处理
- if !copiedString.isEmpty {
- print("检测到新复制的内容: \(copiedString)")
- // 更新记录的内容和计数
- lastPasteboardContent = copiedString
- initialChangeCount = currentChangeCount
- // 处理复制的内容
- self.handleCopiedContent(copiedString)
-
- // 检查访问后的时间延迟
- let timeDelay = Date().timeIntervalSince(startTime)
-
- // 如果访问剪贴板的时间超过一定阈值,可能是因为系统弹出了权限弹窗
- // 通常正常访问剪贴板的时间应该很短,如果超过100毫秒,可能是弹出了弹窗
- if timeDelay > 0.1 {
- self.openPasteTipView()
- } else {
-
- }
- } else {
- // 内容相同或为空,只更新计数
- initialChangeCount = currentChangeCount
- print("剪贴板计数变化但内容未变或为空")
- self.openPasteTipView()
- }
- } else {
- // 剪贴板内容为nil,更新计数
- initialChangeCount = currentChangeCount
- lastPasteboardContent = nil
- print("剪贴板内容被清空")
- }
- }
- }
- }
-
- // 处理复制的内容
- func handleCopiedContent(_ content: String) {
-
- self.helpView.setPasteStr(content: content)
- self.teachView.setPasteStr(content: content)
- }
-
- // 检查键盘权限
- func checkPasteboardPermission() -> Bool {
-
- var hasPasteboardPermission = false
- // iOS 14及以上版本需要检查权限
- if #available(iOS 14.0, *) {
- // 尝试读取剪贴板内容来检查权限
- let pasteboard = UIPasteboard.general
-
- if let _ = pasteboard.string {
- hasPasteboardPermission = true
- print("键盘扩展有剪贴板访问权限")
- } else {
- hasPasteboardPermission = false
- print("键盘扩展没有剪贴板访问权限")
- }
- } else {
- // iOS 14以下版本默认有权限
- hasPasteboardPermission = true
- }
- return hasPasteboardPermission
- }
-
- // 检测系统是否弹出剪贴板权限弹窗
- func detectPasteboardPermissionAlert(completion: @escaping (Bool) -> Void) {
- // 记录访问前的时间
- let startTime = Date()
-
- // 尝试访问剪贴板
- let _ = UIPasteboard.general.string
-
- // 检查访问后的时间延迟
- let timeDelay = Date().timeIntervalSince(startTime)
-
- // 如果访问剪贴板的时间超过一定阈值,可能是因为系统弹出了权限弹窗
- // 通常正常访问剪贴板的时间应该很短,如果超过100毫秒,可能是弹出了弹窗
- if timeDelay > 0.1 {
- completion(true)
- } else {
- completion(false)
- }
- }
-
- // 获取剪贴板内容
- func getPasteboardContent() -> String? {
- let pasteboard = UIPasteboard.general
- return pasteboard.string
- }
-
- // 允许粘贴跳转至设置页
- func jumpToSettings() {
-
- if let url = URL(string: UIApplication.openSettingsURLString) {
- self.openURL(url)
- }
- }
-
- func tipsCloseBtnAction() {
-
- self.helpView.topView.isHidden = true
- self.helpView.topView.snp.updateConstraints { make in
- make.height.equalTo(0)
- }
-
- self.teachView.topView.isHidden = true
- self.teachView.topView.snp.updateConstraints { make in
- make.height.equalTo(0)
- }
-
- self.prologueView.topView.isHidden = true
- self.prologueView.topView.snp.updateConstraints { make in
- make.height.equalTo(0)
- }
-
- self.view.removeConstraint(self.heightConstriaint!)
- self.heightConstriaint = NSLayoutConstraint(item: self.view, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 0.0, constant: UX.keyboardDefaultHeight)
- // newHeightConstriaint.priority = UILayoutPriority(750)
- self.view.addConstraint(self.heightConstriaint!)
- }
- }
- extension KeyboardViewController {
-
- // 打开弹窗
- func openPasteTipView() {
-
- self.helpView.topView.isHidden = false
- self.helpView.topView.snp.updateConstraints { make in
- make.height.equalTo(32)
- }
-
- self.teachView.topView.isHidden = false
- self.teachView.topView.snp.updateConstraints { make in
- make.height.equalTo(32)
- }
-
- self.prologueView.topView.isHidden = false
- self.prologueView.topView.snp.updateConstraints { make in
- make.height.equalTo(32)
- }
-
- self.view.removeConstraint(self.heightConstriaint!)
- self.heightConstriaint = NSLayoutConstraint(item: self.view, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 0.0, constant: UX.keyboardWithTipsHeight)
- // newHeightConstriaint.priority = UILayoutPriority(750)
- self.view.addConstraint(self.heightConstriaint!)
- }
-
- // 检查是否有完全访问权限
- func checkFullAccess() {
-
- let isFullAccess = self.hasFullAccess
-
- if !isFullAccess {
-
- popTipsView()
- }
- }
-
- // 弹出完全访问设置页
- func popTipsView() {
-
- self.tipPopView.isHidden = false
- }
-
- // 跳转登录页
- func popLoginView() {
-
- self.clearPopView()
- self.loginView.isHidden = false
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.login_show)
- }
-
- // 弹出vip页面
- func popVipView() {
-
- self.clearPopView()
- self.vipView.isHidden = false
- GravityEngineSDK.sharedInstance()?.track(KeyboardPoint.vip_show)
- }
-
- // 关闭其他弹出页
- func clearPopView() {
-
- self.vipView.isHidden = true
- self.loginView.isHidden = true
- self.menuView.isHidden = true
- self.exchangeView.isHidden = true
- self.helpView.isHidden = true
- self.teachView.isHidden = true
- self.prologueView.isHidden = true
- }
-
- // 更新主要界面
- func updateMainViewUI() {
-
- clearPopView()
-
- switch chooseType {
- case .help:
- // self.helpView.characterList = self.characterList
- self.helpView.isHidden = false
- self.nowShowView = self.helpView
- self.functionLabel.text = "帮聊"
- break
- case .teach:
- // self.teachView.characterList = self.characterList
- self.teachView.isHidden = false
- self.nowShowView = self.teachView
- self.functionLabel.text = "教你说"
- break
- case .prologue:
- self.prologueView.isHidden = false
- self.nowShowView = self.prologueView
- self.functionLabel.text = "开场白"
- break
- }
- }
-
- // 更新人设列表
- func updateCharacterUI() {
-
- self.helpView.characterList = self.characterList
- self.teachView.characterList = self.characterList
- }
-
- // 更新开场白
- func updatePrologueUI() {
-
- self.prologueView.prologueList = self.prologueList
- }
-
- // 更新键盘选择按钮
- func updateUserChangeBtnUI() {
-
- if self.keyboardList?.count == 1 {
-
- self.keyboardList?[0].isChoose = true
- self.chooseKeyboard = self.keyboardList?.first
- } else {
-
- if let keyboardList = self.keyboardList {
- for keyboard in keyboardList {
- if keyboard.isChoose == true {
- self.chooseKeyboard = keyboard
- }
- }
- }
- }
-
- if let chooseKeyboard = self.chooseKeyboard {
- self.userChangeLabel.text = chooseKeyboard.name
- self.heartLabel.text = "\(chooseKeyboard.intimacy ?? 0)%"
- }
- }
-
- func setUI() {
- self.view.addSubview(bgImageView)
- bgImageView.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- self.view.addSubview(menuBtn)
- menuBtn.snp.makeConstraints { make in
- make.size.equalTo(CGSize(width: 32, height: 32))
- make.left.equalTo(13)
- make.top.equalTo(17)
- }
-
- self.view.addSubview(heartAnimation)
- heartAnimation.snp.makeConstraints { make in
- make.size.equalTo(CGSize(width: 35, height: 30))
- make.right.equalTo(-10)
- make.centerY.equalTo(menuBtn.snp.centerY)
- }
- heartAnimation.play()
-
- self.view.addSubview(heartLabel)
- heartLabel.snp.makeConstraints { make in
- make.centerY.equalTo(heartAnimation.snp.centerY).offset(-2)
- make.centerX.equalTo(heartAnimation.snp.centerX)
- }
-
- self.view.addSubview(exchangeBtn)
- exchangeBtn.snp.makeConstraints { make in
- make.size.equalTo(CGSize(width: 34, height: 34))
- make.right.equalTo(heartAnimation.snp.left).offset(-10)
- make.centerY.equalTo(menuBtn.snp.centerY)
- }
-
- self.view.addSubview(teachView)
- teachView.snp.makeConstraints { make in
- make.left.right.bottom.equalTo(0)
- make.top.equalTo(60)
- }
-
- self.view.addSubview(helpView)
- helpView.snp.makeConstraints { make in
- make.left.right.bottom.equalTo(0)
- make.top.equalTo(60)
- }
-
- self.view.addSubview(prologueView)
- prologueView.snp.makeConstraints { make in
- make.left.right.bottom.equalTo(0)
- make.top.equalTo(60)
- }
-
- self.view.addSubview(chooseView)
- chooseView.snp.makeConstraints { make in
- make.width.equalTo(185)
- make.height.equalTo(34)
- make.left.equalTo(menuBtn.snp.right).offset(9)
- make.centerY.equalTo(menuBtn.snp.centerY)
- }
-
- self.view.addSubview(exchangeView)
- exchangeView.snp.makeConstraints { make in
- make.top.equalTo(menuBtn.snp.bottom)
- make.left.right.bottom.equalTo(0)
- }
-
- self.view.addSubview(menuView)
- menuView.snp.makeConstraints { make in
- make.left.right.bottom.equalTo(0)
- make.top.equalTo(menuBtn.snp.bottom)
- }
-
- self.view.addSubview(loginView)
- loginView.snp.makeConstraints { make in
- make.top.left.right.bottom.equalTo(0)
- }
-
- self.view.addSubview(vipView)
- vipView.snp.makeConstraints { make in
- make.top.left.right.bottom.equalTo(0)
- }
-
- self.view.addSubview(tipPopView)
- tipPopView.snp.makeConstraints { make in
- make.left.right.bottom.equalTo(0)
- make.top.equalTo(56)
- }
-
- chooseView.addSubview(functionView)
- functionView.snp.makeConstraints { make in
- make.top.left.equalTo(1)
- make.bottom.equalTo(-1)
- make.width.equalTo(85)
- }
-
- functionView.addSubview(functionLabel)
- functionLabel.snp.makeConstraints { make in
- make.left.equalTo(12)
- make.centerY.equalToSuperview()
- }
-
- functionView.addSubview(arrowIcon)
- arrowIcon.snp.makeConstraints { make in
- make.size.equalTo(CGSize(width: 18, height: 18))
- make.centerY.equalToSuperview()
- make.right.equalTo(-5)
- }
-
- chooseView.addSubview(userChangeView)
- userChangeView.snp.makeConstraints { make in
- make.left.equalTo(functionView.snp.right)
- make.right.equalToSuperview()
- make.top.bottom.equalTo(0)
- }
-
- userChangeView.addSubview(userChangeIcon)
- userChangeIcon.snp.makeConstraints { make in
- make.size.equalTo(CGSize(width: 12, height: 12))
- make.centerY.equalToSuperview()
- make.right.equalTo(-8)
- }
-
- userChangeView.addSubview(userChangeLabel)
- userChangeLabel.snp.makeConstraints { make in
- make.right.equalTo(userChangeIcon.snp.left).offset(-4)
- make.left.equalTo(4)
- make.centerY.equalToSuperview()
- }
-
- // chooseView.addSubview(userChangeBtn)
- // userChangeBtn.snp.makeConstraints { make in
- // make.left.equalTo(functionView.snp.right)
- // make.right.equalToSuperview()
- // make.top.bottom.equalTo(0)
- // }
-
- self.nowShowView = self.helpView
- }
- }
|