KeyboardViewController.swift 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258
  1. //
  2. // KeyboardViewController.swift
  3. // AiKeyboard
  4. //
  5. // Created by Destiny on 2025/4/23.
  6. //
  7. import UIKit
  8. import Lottie
  9. import SnapKit
  10. import MarqueeLabel
  11. enum KeyboardType: Int {
  12. case help = 0 // 帮聊
  13. case teach = 1 // 教你说
  14. case prologue = 2 // 开场白
  15. }
  16. class KeyboardViewController: UIInputViewController {
  17. struct UX {
  18. static let keyboardHeight = 318.0
  19. }
  20. // 选择的键盘类型
  21. var chooseType: KeyboardType = .help {
  22. didSet {
  23. self.updateMainViewUI()
  24. }
  25. }
  26. // 键盘列表
  27. var keyboardList: [KeyboardModel]?
  28. // 选择的键盘
  29. var chooseKeyboard: KeyboardModel?
  30. // 人设列表
  31. var characterList: [CharacterModel]?
  32. // 开场白列表
  33. var prologueList: [PrologueModel]?
  34. // 开始持续删除
  35. private var deleteTimer: Timer?
  36. private var deleteAcceleration: TimeInterval = 0.1
  37. var heightConstriaint: NSLayoutConstraint?
  38. lazy var bgImageView: UIImageView = {
  39. let imageView = UIImageView()
  40. imageView.image = UIImage(named: "keyboard_bg")
  41. return imageView
  42. }()
  43. lazy var menuBtn: UIButton = {
  44. let button = UIButton()
  45. button.setImage(UIImage(named: "keyboard_menu_btn"), for: .normal)
  46. button.addTarget(self, action: #selector(menuBtnClickAction), for: .touchUpInside)
  47. return button
  48. }()
  49. lazy var heartAnimation: LottieAnimationView = {
  50. let animate = LottieAnimationView(name: "heart")
  51. animate.loopMode = .loop
  52. animate.backgroundColor = .clear
  53. animate.isUserInteractionEnabled = true
  54. let tap = UITapGestureRecognizer(target: self, action: #selector(heartBtnClickAction))
  55. animate.addGestureRecognizer(tap)
  56. return animate
  57. }()
  58. lazy var heartLabel: UILabel = {
  59. let label = UILabel()
  60. label.text = "30%"
  61. label.font = .boldSystemFont(ofSize: 10)
  62. label.textColor = .white
  63. label.isUserInteractionEnabled = true
  64. let tap = UITapGestureRecognizer(target: self, action: #selector(heartBtnClickAction))
  65. label.addGestureRecognizer(tap)
  66. return label
  67. }()
  68. lazy var exchangeBtn: UIButton = {
  69. let button = UIButton()
  70. button.setImage(UIImage(named: "keyboard_exchange_btn"), for: .normal)
  71. button.addTarget(self, action: #selector(exchangeBtnClickAction), for: .touchUpInside)
  72. return button
  73. }()
  74. lazy var chooseView: UIView = {
  75. let view = UIView()
  76. view.backgroundColor = .white
  77. view.layer.cornerRadius = 17
  78. return view
  79. }()
  80. lazy var functionView: UIView = {
  81. let view = UIView()
  82. view.backgroundColor = .hexStringColor(hexString: "#DDCFFD")
  83. view.layer.cornerRadius = 16
  84. view.isUserInteractionEnabled = true
  85. let tap = UITapGestureRecognizer(target: self, action: #selector(functionBtnClickAction))
  86. view.addGestureRecognizer(tap)
  87. return view
  88. }()
  89. lazy var functionLabel: UILabel = {
  90. let label = UILabel()
  91. label.text = "帮聊"
  92. label.font = .boldSystemFont(ofSize: 12)
  93. label.textColor = .hexStringColor(hexString: "#000000", alpha: 0.8)
  94. return label
  95. }()
  96. lazy var arrowIcon: UIImageView = {
  97. let imageView = UIImageView()
  98. imageView.image = UIImage(named: "icon_arrow_down")
  99. return imageView
  100. }()
  101. lazy var userChangeBtn: UIButton = {
  102. let btn = UIButton()
  103. btn.setTitle("小蕾", for: .normal)
  104. btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
  105. btn.setTitleColor(.hexStringColor(hexString: "#000000").withAlphaComponent(0.8), for: .normal)
  106. btn.setImage(UIImage(named: "keyboard_user_change_icon"), for: .normal)
  107. btn.setImageTitleLayout(.imgRight, spacing: 8)
  108. btn.addTarget(self, action: #selector(changeBtnClickAction), for: .touchUpInside)
  109. return btn
  110. }()
  111. lazy var userChangeView: UIView = {
  112. let view = UIView()
  113. let tap = UITapGestureRecognizer(target: self, action: #selector(changeBtnClickAction))
  114. view.addGestureRecognizer(tap)
  115. return view
  116. }()
  117. lazy var userChangeLabel: MarqueeLabel = {
  118. let label = MarqueeLabel(frame: .zero, duration: 3.0, fadeLength: 0)
  119. label.type = .leftRight
  120. label.text = "通用键盘"
  121. label.font = .systemFont(ofSize: 12)
  122. label.textColor = .hexStringColor(hexString: "#000000")
  123. label.textAlignment = .center
  124. return label
  125. }()
  126. lazy var userChangeIcon: UIImageView = {
  127. let icon = UIImageView()
  128. icon.image = UIImage(named: "keyboard_user_change_icon")
  129. return icon
  130. }()
  131. lazy var helpView: KeyboardHelpView = {
  132. let view = KeyboardHelpView()
  133. view.delegate = self
  134. view.helpDelegate = self
  135. return view
  136. }()
  137. lazy var teachView: KeyboardTeachView = {
  138. let view = KeyboardTeachView()
  139. view.delegate = self
  140. view.teachDelegate = self
  141. view.isHidden = true
  142. return view
  143. }()
  144. lazy var prologueView: KeyboardPrologueView = {
  145. let view = KeyboardPrologueView()
  146. view.delegate = self
  147. view.prologueDelegate = self
  148. view.isHidden = true
  149. return view
  150. }()
  151. lazy var exchangeView: KeyboardExchangeView = {
  152. let view = KeyboardExchangeView()
  153. view.isHidden = true
  154. view.delegate = self
  155. return view
  156. }()
  157. lazy var menuView: KeyboardMenuView = {
  158. let view = KeyboardMenuView()
  159. view.isHidden = true
  160. view.delegate = self
  161. return view
  162. }()
  163. lazy var loginView: KeyboardLoginTipView = {
  164. let view = KeyboardLoginTipView()
  165. view.isHidden = true
  166. view.loginBtnClosure = {
  167. self.navigateToLogin()
  168. }
  169. view.backBtnClosure = {
  170. self.clearPopView()
  171. self.loginView.isHidden = true
  172. self.nowShowView?.isHidden = false
  173. }
  174. return view
  175. }()
  176. lazy var vipView: KeyboardVipTipView = {
  177. let view = KeyboardVipTipView()
  178. view.isHidden = true
  179. view.unlockBtnClosure = {
  180. self.navigateToMembership()
  181. }
  182. view.backBtnClosure = {
  183. self.clearPopView()
  184. self.vipView.isHidden = true
  185. self.nowShowView?.isHidden = false
  186. }
  187. return view
  188. }()
  189. var nowShowView: UIView?
  190. // 命令检查定时器
  191. private var guideCheckTimer: Timer?
  192. override func viewWillAppear(_ animated: Bool) {
  193. super.viewWillAppear(animated)
  194. self.prepareHeightConstraint()
  195. }
  196. override func viewDidLoad() {
  197. super.viewDidLoad()
  198. setUI()
  199. checkFullAccess()
  200. KeyboardApi.initParams()
  201. let isFullAccess = self.hasFullAccess
  202. if isFullAccess {
  203. requestKeyboardList()
  204. requestPrologueList()
  205. }
  206. startMonitoringPasteboard()
  207. startListenGuide()
  208. // self.nextKeyboardButton.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
  209. // self.nextKeyboardButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
  210. }
  211. override func viewWillDisappear(_ animated: Bool) {
  212. super.viewWillDisappear(animated)
  213. }
  214. override func viewWillLayoutSubviews() {
  215. super.viewWillLayoutSubviews()
  216. }
  217. override func updateViewConstraints() {
  218. super.updateViewConstraints()
  219. if self.view.frame.size.width == 0 && self.view.frame.size.height == 0 {
  220. return
  221. }
  222. self.prepareHeightConstraint()
  223. }
  224. override func textWillChange(_ textInput: UITextInput?) {
  225. // The app is about to change the document's contents. Perform any preparation here.
  226. }
  227. override func textDidChange(_ textInput: UITextInput?) {
  228. // The app has just changed the document's contents, the document context has been updated.
  229. var textColor: UIColor
  230. let proxy = self.textDocumentProxy
  231. if proxy.keyboardAppearance == UIKeyboardAppearance.dark {
  232. textColor = UIColor.white
  233. } else {
  234. textColor = UIColor.black
  235. }
  236. }
  237. func prepareHeightConstraint() {
  238. if self.heightConstriaint == nil {
  239. if let view = self.view {
  240. self.heightConstriaint = NSLayoutConstraint(item: view, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 0.0, constant: UX.keyboardHeight)
  241. self.heightConstriaint?.priority = UILayoutPriority(750)
  242. self.view.addConstraint(self.heightConstriaint!)
  243. }
  244. } else {
  245. self.heightConstriaint?.constant = UX.keyboardHeight
  246. }
  247. }
  248. // 开始监听是否需要打开引导页
  249. func startListenGuide() {
  250. let isNotFirstOpen = UserDefaults.standard.bool(forKey: "isNotFirstOpenKey")
  251. if !isNotFirstOpen {
  252. // 创建定时器定期检查命令
  253. guideCheckTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
  254. self?.popGuideView()
  255. }
  256. }
  257. }
  258. func popGuideView() {
  259. let hasFullAccess = self.hasFullAccess
  260. let showGuide = KeyboardSharedDataManager.shared.getIsShowGuide()
  261. if hasFullAccess && (showGuide == true) {
  262. let guideView = KeyboardGuideView()
  263. self.view.addSubview(guideView)
  264. guideView.snp.makeConstraints { make in
  265. make.top.left.right.bottom.equalTo(0)
  266. }
  267. UserDefaults.standard.set(true, forKey: "isNotFirstOpenKey")
  268. guideCheckTimer?.invalidate()
  269. guideCheckTimer = nil
  270. }
  271. }
  272. }
  273. // MARK: - 网络请求
  274. extension KeyboardViewController {
  275. // 获取键盘列表
  276. func requestKeyboardList() {
  277. KeyboardNetworkManager.request(.keyboard_list(params: [:])) { response in
  278. if let keyboardInfos = try? response.mapArray(KeyboardModel.self, atKeyPath: "data.keyboardInfos") {
  279. self.keyboardList = keyboardInfos
  280. self.updateUserChangeBtnUI()
  281. self.requestCharacterList()
  282. print("成功解析数组,数量:\(keyboardInfos.count)")
  283. }
  284. } fail: { code, error in
  285. self.view.toast(text: error)
  286. print(error)
  287. }
  288. }
  289. // 获取键盘人设列表
  290. func requestCharacterList() {
  291. var params: [String: Any] = [String: Any]()
  292. if let chooseKeyboard = self.chooseKeyboard {
  293. params = ["keyboardId": chooseKeyboard.id ?? ""]
  294. }
  295. KeyboardNetworkManager.request(.character_list(params: params)) { response in
  296. if let characterInfos = try? response.mapArray(CharacterModel.self, atKeyPath: "data.characterInfos") {
  297. self.characterList = characterInfos
  298. self.updateCharacterUI()
  299. print("成功解析数组,数量:\(characterInfos.count)")
  300. }
  301. } fail: { code, error in
  302. self.view.toast(text: error)
  303. print(error)
  304. }
  305. }
  306. // 获取键盘开场白列表
  307. func requestPrologueList() {
  308. KeyboardNetworkManager.request(.prologue_list(params: [:])) { response in
  309. if let prologueList = try? response.mapArray(PrologueModel.self, atKeyPath: "data.prologues") {
  310. self.prologueList = prologueList
  311. self.updatePrologueUI()
  312. }
  313. } fail: { code, error in
  314. self.view.toast(text: error)
  315. print(error)
  316. }
  317. }
  318. // 选择键盘
  319. func requestChooseKeyboard(keyboardId: String, success: @escaping (() -> Void)) {
  320. var params: [String: Any] = [String: Any]()
  321. params = ["keyboardId": keyboardId]
  322. KeyboardNetworkManager.request(.keyboard_choose(params: params)) { response in
  323. self.view.toast(text: "保存成功")
  324. success()
  325. } fail: { code, error in
  326. self.view.toast(text: error)
  327. print(error)
  328. }
  329. }
  330. // 请求获取超会回
  331. func requestSuperReply(characterId: String, content: String, complete: @escaping ((String) -> ())) {
  332. var params: [String: Any] = [String: Any]()
  333. params = ["keyboardId": self.chooseKeyboard?.id ?? "",
  334. "characterId": characterId,
  335. "content": content]
  336. KeyboardNetworkManager.request(.chat_super_reply(params: params)) { response in
  337. if let contentModel = try? response.mapObject(ReplyModel.self, atKeyPath: "data") {
  338. complete(contentModel.content ?? "")
  339. } else {
  340. complete("")
  341. }
  342. } fail: { code, error in
  343. self.view.toast(text: error)
  344. print(error)
  345. complete("")
  346. if code == 1006 {
  347. // 弹出登录页
  348. self.popLoginView()
  349. } else if code == 1008 {
  350. // 弹出会员页
  351. self.popVipView()
  352. }
  353. }
  354. }
  355. // 请求获取超会说
  356. func requestSuperSpeak(characterId: String, content: String, complete: @escaping (([String]) -> ())) {
  357. var params: [String: Any] = [String: Any]()
  358. params = ["keyboardId": self.chooseKeyboard?.id ?? "",
  359. "characterId": characterId,
  360. "content": content]
  361. KeyboardNetworkManager.request(.chat_super_speak(params: params)) { response in
  362. if let contentModel = try? response.mapObject(SpeakModel.self, atKeyPath: "data"), let list = contentModel.list {
  363. complete(list)
  364. } else {
  365. complete([String]())
  366. }
  367. } fail: { code, error in
  368. self.view.toast(text: error)
  369. print(error)
  370. complete([String]())
  371. if code == 1006 {
  372. // 弹出登录页
  373. self.popLoginView()
  374. } else if code == 1008 {
  375. // 弹出会员页
  376. self.popVipView()
  377. }
  378. }
  379. }
  380. // 请求获取超会说
  381. func requestSuperPrologue(name: String, complete: @escaping (([String]) -> ())) {
  382. var params: [String: Any] = [String: Any]()
  383. params = ["name": name]
  384. KeyboardNetworkManager.request(.chat_super_prologue(params: params)) { response in
  385. if let contentModel = try? response.mapObject(SpeakModel.self, atKeyPath: "data"), let list = contentModel.list {
  386. complete(list)
  387. } else {
  388. complete([String]())
  389. }
  390. } fail: { code, error in
  391. self.view.toast(text: error)
  392. print(error)
  393. complete([String]())
  394. if code == 1006 {
  395. // 弹出登录页
  396. self.popLoginView()
  397. } else if code == 1008 {
  398. // 弹出会员页
  399. self.popVipView()
  400. }
  401. }
  402. }
  403. }
  404. // MARK: - 点击事件
  405. extension KeyboardViewController: KeyboardMenuViewDelegate, KeyboardExchangeViewDelegate {
  406. // 亲密度按钮点击
  407. @objc func heartBtnClickAction() {
  408. let url = URL(string: "com.qihuan.zhuiaijianpan:///intimacy")!
  409. openURL(url)
  410. }
  411. // 切换系统键盘
  412. @objc func exchangeBtnClickAction() {
  413. self.advanceToNextInputMode()
  414. }
  415. // 功能按钮选择点击
  416. @objc func functionBtnClickAction() {
  417. KeyboardFunctionPopView.show(view: self.view, selectType: self.chooseType) { type in
  418. self.chooseType = type
  419. }
  420. }
  421. // 菜单按钮点击
  422. @objc func menuBtnClickAction() {
  423. clearPopView()
  424. self.menuView.isHidden = false
  425. }
  426. // 菜单按钮返回
  427. func menuBackBtnClickAction() {
  428. clearPopView()
  429. self.nowShowView?.isHidden = false
  430. }
  431. // 会员按钮点击
  432. func vipBtnClickAction() {
  433. navigateToMembership()
  434. }
  435. // 定制人设
  436. func diyBtnClickAction() {
  437. let url = URL(string: "com.qihuan.zhuiaijianpan:///character/custom")!
  438. openURL(url)
  439. }
  440. // 人设市场
  441. func marketBtnClickAction() {
  442. let url = URL(string: "com.qihuan.zhuiaijianpan:///character/market")!
  443. openURL(url)
  444. }
  445. // 跳转到主应用的登录页面
  446. @objc func navigateToLogin() {
  447. let url = URL(string: "com.qihuan.zhuiaijianpan:///login")!
  448. openURL(url)
  449. }
  450. // 跳转到主应用的会员页面
  451. @objc func navigateToMembership() {
  452. let url = URL(string: "com.qihuan.zhuiaijianpan:///member")!
  453. openURL(url)
  454. }
  455. @objc @discardableResult private func openURL(_ url: URL) -> Bool {
  456. var responder: UIResponder? = self
  457. while responder != nil {
  458. if let application = responder as? UIApplication {
  459. if #available(iOS 18.0, *) {
  460. application.open(url, options: [:], completionHandler: nil)
  461. return true
  462. } else {
  463. return application.perform(#selector(openURL(_:)), with: url) != nil
  464. }
  465. }
  466. responder = responder?.next
  467. }
  468. return false
  469. }
  470. // // 通用的打开URL方法
  471. // private func openURL(_ url: URL) {
  472. //// var responder = self as UIResponder?
  473. //// while (responder != nil && !(responder is UIApplication)) {
  474. //// responder = responder?.next
  475. //// }
  476. //// if responder != nil {
  477. //// let selectorOpenURL = sel_registerName("openURL:options:completionHandler:")
  478. //// if responder!.responds(to: selectorOpenURL) {
  479. //// self.dismissKeyboard()
  480. //// responder?.perform(selectorOpenURL, with: [url, nil, nil])
  481. //// }
  482. //// }
  483. //
  484. // var responder: UIResponder? = self
  485. // while responder != nil {
  486. // if let application = responder as? UIApplication {
  487. // if #available(iOS 18.0, *) {
  488. // application.open(url, options: [:], completionHandler: nil)
  489. // } else {
  490. // application.perform(#selector(openURL(_:)), with: url)
  491. // }
  492. // }
  493. // responder = responder?.next
  494. // }
  495. // // 不知道为什么无法使用
  496. //// self.extensionContext?.open(url, completionHandler: nil)
  497. // }
  498. // 选择键盘按钮点击
  499. @objc func changeBtnClickAction() {
  500. clearPopView()
  501. self.exchangeView.keyboardList = self.keyboardList
  502. self.exchangeView.isHidden = false
  503. }
  504. // 选择键盘后返回
  505. func exchangeViewBackClickAction() {
  506. clearPopView()
  507. self.nowShowView?.isHidden = false
  508. }
  509. // 点击保存按钮
  510. func exchangeViewSaveClickAction(keyboardList: [KeyboardModel], success: @escaping (() -> ())) {
  511. var keyboardId = ""
  512. var selectKeyboard: KeyboardModel?
  513. for keyboard in keyboardList {
  514. if keyboard.isChoose == true {
  515. keyboardId = keyboard.id ?? ""
  516. selectKeyboard = keyboard
  517. break
  518. }
  519. }
  520. if let selectKeyboard = selectKeyboard {
  521. // 当为系统键盘时不需要调接口
  522. if selectKeyboard.type == "system" {
  523. self.keyboardList = keyboardList
  524. self.chooseKeyboard = selectKeyboard
  525. self.requestCharacterList()
  526. success()
  527. self.userChangeLabel.text = self.chooseKeyboard?.name
  528. self.clearPopView()
  529. self.nowShowView?.isHidden = false
  530. } else {
  531. requestChooseKeyboard(keyboardId: keyboardId) {
  532. self.keyboardList = keyboardList
  533. self.chooseKeyboard = selectKeyboard
  534. self.requestCharacterList()
  535. success()
  536. self.userChangeLabel.text = self.chooseKeyboard?.name
  537. self.clearPopView()
  538. self.nowShowView?.isHidden = false
  539. }
  540. }
  541. }
  542. }
  543. }
  544. extension KeyboardViewController: KeyboardHelpViewDelegate, KeyboardTeachViewDelegate, KeyboardPrologueViewDelegate {
  545. // 帮聊点击cell
  546. func helpCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping ((Bool) -> ())) {
  547. // 判断是否登录
  548. let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
  549. if !isLogin {
  550. popLoginView()
  551. complete(false)
  552. return
  553. }
  554. self.requestSuperReply(characterId: characterId, content: content) { text in
  555. self.insertText(text)
  556. complete(true)
  557. }
  558. }
  559. // 教你说点击cell
  560. func teachCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (([String], Bool, Bool) -> ())) {
  561. // 判断是否登录
  562. let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
  563. if !isLogin {
  564. popLoginView()
  565. complete([String](), false, false)
  566. return
  567. } else {
  568. complete([String](), true, false)
  569. }
  570. self.requestSuperSpeak(characterId: characterId, content: content) { list in
  571. complete(list, true, true)
  572. }
  573. }
  574. // 教你说点击 TableViewCell
  575. func teachTableViewDidSelectItem(content: String) {
  576. self.insertText(content)
  577. }
  578. // 开场白点击cell
  579. func prologueCollectionViewDidSelectItem(name: String, complete: @escaping (([String], Bool, Bool) -> ())) {
  580. // 判断是否登录
  581. let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
  582. if !isLogin {
  583. popLoginView()
  584. complete([String](), false, false)
  585. return
  586. } else {
  587. complete([String](), true, false)
  588. }
  589. self.requestSuperPrologue(name: name) { list in
  590. complete(list, true, true)
  591. }
  592. }
  593. // 开场白点击 TableViewCell
  594. func prologueTableViewDidSelectItem(content: String) {
  595. self.insertText(content)
  596. }
  597. // 点击添加人设
  598. func addCharacterJump() {
  599. self.marketBtnClickAction()
  600. }
  601. }
  602. // MARK: - 处理键盘操作
  603. extension KeyboardViewController: KeyboardBaseViewDelegate {
  604. // 插入文字
  605. func insertText(_ text: String) {
  606. self.textDocumentProxy.insertText(text)
  607. }
  608. // 点击粘贴按钮
  609. func pasteBtnClickAction() {
  610. detectPasteboardPermissionAlert { isShowing in
  611. if isShowing {
  612. print("系统正在显示剪贴板权限弹窗")
  613. self.view.toast(text: "请允许访问剪贴板")
  614. self.openPasteTipView()
  615. }
  616. if let content = self.getPasteboardContent() {
  617. print("获取到剪贴板内容: \(content)")
  618. self.view.toast(text: "获取到剪贴板内容")
  619. self.helpView.setPasteStr(content: content)
  620. self.teachView.setPasteStr(content: content)
  621. } else {
  622. print("无法获取剪贴板内容")
  623. self.view.toast(text: "无法获取剪贴板内容")
  624. }
  625. }
  626. }
  627. // 删除按钮
  628. func deleteBtnClickAction() {
  629. self.textDocumentProxy.deleteBackward()
  630. }
  631. // 清除按钮
  632. func clearBtnClickAction() {
  633. // 获取当前文本
  634. let proxy = self.textDocumentProxy
  635. // 清空所有文本
  636. for _ in 0..<100 {
  637. proxy.deleteBackward()
  638. }
  639. }
  640. // 发送按钮
  641. func sendBtnClickAction() {
  642. self.textDocumentProxy.insertText("\n")
  643. }
  644. func deleteBtnLongPressBegin() {
  645. // 停止已有的定时器
  646. stopContinuousDelete()
  647. // 创建新的定时器,初始间隔为0.5秒
  648. deleteTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(continuousDeleteAction), userInfo: nil, repeats: false)
  649. }
  650. func deleteBtnLongPressEnd() {
  651. stopContinuousDelete()
  652. }
  653. // 停止删除
  654. func stopContinuousDelete() {
  655. deleteTimer?.invalidate()
  656. deleteTimer = nil
  657. deleteAcceleration = 0.1 // 重置加速度
  658. }
  659. @objc func continuousDeleteAction() {
  660. // 执行删除操作
  661. self.textDocumentProxy.deleteBackward()
  662. // 停止当前定时器
  663. deleteTimer?.invalidate()
  664. // 加速删除(最快0.05秒一次)
  665. let newInterval = max(0.05, 0.5 - deleteAcceleration)
  666. deleteAcceleration += 0.05 // 每次加速0.05秒
  667. // 创建新的更快的定时器
  668. deleteTimer = Timer.scheduledTimer(timeInterval: newInterval, target: self, selector: #selector(continuousDeleteAction), userInfo: nil, repeats: false)
  669. }
  670. // 监听剪贴板变化
  671. func startMonitoringPasteboard() {
  672. // 记录当前剪贴板变化计数
  673. var initialChangeCount = UIPasteboard.general.changeCount
  674. // 记录上一次的剪贴板内容
  675. var lastPasteboardContent: String? = UIPasteboard.general.string
  676. // 创建定时器定期检查剪贴板变化
  677. Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] timer in
  678. guard let self = self else {
  679. timer.invalidate()
  680. return
  681. }
  682. let currentChangeCount = UIPasteboard.general.changeCount
  683. if currentChangeCount != initialChangeCount {
  684. // 记录访问前的时间
  685. let startTime = Date()
  686. // 剪贴板计数已变化,检查内容是否真的变化
  687. if let copiedString = UIPasteboard.general.string {
  688. // 只有当内容真的不同时才处理
  689. if !copiedString.isEmpty {
  690. print("检测到新复制的内容: \(copiedString)")
  691. // 更新记录的内容和计数
  692. lastPasteboardContent = copiedString
  693. initialChangeCount = currentChangeCount
  694. // 处理复制的内容
  695. self.handleCopiedContent(copiedString)
  696. // 检查访问后的时间延迟
  697. let timeDelay = Date().timeIntervalSince(startTime)
  698. // 如果访问剪贴板的时间超过一定阈值,可能是因为系统弹出了权限弹窗
  699. // 通常正常访问剪贴板的时间应该很短,如果超过100毫秒,可能是弹出了弹窗
  700. if timeDelay > 0.1 {
  701. self.openPasteTipView()
  702. } else {
  703. }
  704. } else {
  705. // 内容相同或为空,只更新计数
  706. initialChangeCount = currentChangeCount
  707. print("剪贴板计数变化但内容未变或为空")
  708. self.openPasteTipView()
  709. }
  710. } else {
  711. // 剪贴板内容为nil,更新计数
  712. initialChangeCount = currentChangeCount
  713. lastPasteboardContent = nil
  714. print("剪贴板内容被清空")
  715. }
  716. }
  717. }
  718. }
  719. // 处理复制的内容
  720. func handleCopiedContent(_ content: String) {
  721. self.helpView.setPasteStr(content: content)
  722. self.teachView.setPasteStr(content: content)
  723. }
  724. // 检查键盘权限
  725. func checkPasteboardPermission() -> Bool {
  726. var hasPasteboardPermission = false
  727. // iOS 14及以上版本需要检查权限
  728. if #available(iOS 14.0, *) {
  729. // 尝试读取剪贴板内容来检查权限
  730. let pasteboard = UIPasteboard.general
  731. if let _ = pasteboard.string {
  732. hasPasteboardPermission = true
  733. print("键盘扩展有剪贴板访问权限")
  734. } else {
  735. hasPasteboardPermission = false
  736. print("键盘扩展没有剪贴板访问权限")
  737. }
  738. } else {
  739. // iOS 14以下版本默认有权限
  740. hasPasteboardPermission = true
  741. }
  742. return hasPasteboardPermission
  743. }
  744. // 检测系统是否弹出剪贴板权限弹窗
  745. func detectPasteboardPermissionAlert(completion: @escaping (Bool) -> Void) {
  746. // 记录访问前的时间
  747. let startTime = Date()
  748. // 尝试访问剪贴板
  749. let _ = UIPasteboard.general.string
  750. // 检查访问后的时间延迟
  751. let timeDelay = Date().timeIntervalSince(startTime)
  752. // 如果访问剪贴板的时间超过一定阈值,可能是因为系统弹出了权限弹窗
  753. // 通常正常访问剪贴板的时间应该很短,如果超过100毫秒,可能是弹出了弹窗
  754. if timeDelay > 0.1 {
  755. completion(true)
  756. } else {
  757. completion(false)
  758. }
  759. }
  760. // 获取剪贴板内容
  761. func getPasteboardContent() -> String? {
  762. let pasteboard = UIPasteboard.general
  763. return pasteboard.string
  764. }
  765. // 允许粘贴跳转至设置页
  766. func jumpToSettings() {
  767. if let url = URL(string: UIApplication.openSettingsURLString) {
  768. self.openURL(url)
  769. }
  770. }
  771. func tipsCloseBtnAction() {
  772. self.helpView.topView.isHidden = true
  773. self.helpView.topView.snp.updateConstraints { make in
  774. make.height.equalTo(0)
  775. }
  776. self.teachView.topView.isHidden = true
  777. self.teachView.topView.snp.updateConstraints { make in
  778. make.height.equalTo(0)
  779. }
  780. self.prologueView.topView.isHidden = true
  781. self.prologueView.topView.snp.updateConstraints { make in
  782. make.height.equalTo(0)
  783. }
  784. }
  785. }
  786. extension KeyboardViewController {
  787. // 打开弹窗
  788. func openPasteTipView() {
  789. self.helpView.topView.isHidden = false
  790. self.helpView.topView.snp.updateConstraints { make in
  791. make.height.equalTo(32)
  792. }
  793. self.teachView.topView.isHidden = false
  794. self.teachView.topView.snp.updateConstraints { make in
  795. make.height.equalTo(32)
  796. }
  797. self.prologueView.topView.isHidden = false
  798. self.prologueView.topView.snp.updateConstraints { make in
  799. make.height.equalTo(32)
  800. }
  801. }
  802. // 检查是否有完全访问权限
  803. func checkFullAccess() {
  804. let isFullAccess = self.hasFullAccess
  805. if !isFullAccess {
  806. let tipView = KeyboardTipsPopView()
  807. tipView.settingBtnClosure = {
  808. if let url = URL(string: UIApplication.openSettingsURLString) {
  809. self.openURL(url)
  810. }
  811. }
  812. self.view.addSubview(tipView)
  813. tipView.snp.makeConstraints { make in
  814. make.left.right.bottom.equalTo(0)
  815. make.top.equalTo(56)
  816. }
  817. }
  818. }
  819. // 跳转登录页
  820. func popLoginView() {
  821. self.clearPopView()
  822. self.loginView.isHidden = false
  823. }
  824. // 弹出vip页面
  825. func popVipView() {
  826. self.clearPopView()
  827. self.vipView.isHidden = false
  828. }
  829. // 关闭其他弹出页
  830. func clearPopView() {
  831. self.vipView.isHidden = true
  832. self.loginView.isHidden = true
  833. self.menuView.isHidden = true
  834. self.exchangeView.isHidden = true
  835. self.helpView.isHidden = true
  836. self.teachView.isHidden = true
  837. self.prologueView.isHidden = true
  838. }
  839. // 更新主要界面
  840. func updateMainViewUI() {
  841. clearPopView()
  842. switch chooseType {
  843. case .help:
  844. // self.helpView.characterList = self.characterList
  845. self.helpView.isHidden = false
  846. self.nowShowView = self.helpView
  847. self.functionLabel.text = "帮聊"
  848. break
  849. case .teach:
  850. // self.teachView.characterList = self.characterList
  851. self.teachView.isHidden = false
  852. self.nowShowView = self.teachView
  853. self.functionLabel.text = "教你说"
  854. break
  855. case .prologue:
  856. self.prologueView.isHidden = false
  857. self.nowShowView = self.prologueView
  858. self.functionLabel.text = "开场白"
  859. break
  860. }
  861. }
  862. // 更新人设列表
  863. func updateCharacterUI() {
  864. self.helpView.characterList = self.characterList
  865. self.teachView.characterList = self.characterList
  866. }
  867. // 更新开场白
  868. func updatePrologueUI() {
  869. self.prologueView.prologueList = self.prologueList
  870. }
  871. // 更新键盘选择按钮
  872. func updateUserChangeBtnUI() {
  873. if self.keyboardList?.count == 1 {
  874. self.keyboardList?[0].isChoose = true
  875. self.chooseKeyboard = self.keyboardList?.first
  876. } else {
  877. if let keyboardList = self.keyboardList {
  878. for keyboard in keyboardList {
  879. if keyboard.isChoose == true {
  880. self.chooseKeyboard = keyboard
  881. }
  882. }
  883. }
  884. }
  885. if let chooseKeyboard = self.chooseKeyboard {
  886. self.userChangeLabel.text = chooseKeyboard.name
  887. self.heartLabel.text = "\(chooseKeyboard.intimacy ?? 0)%"
  888. }
  889. }
  890. func setUI() {
  891. self.view.addSubview(bgImageView)
  892. bgImageView.snp.makeConstraints { make in
  893. make.edges.equalToSuperview()
  894. }
  895. self.view.addSubview(menuBtn)
  896. menuBtn.snp.makeConstraints { make in
  897. make.size.equalTo(CGSize(width: 32, height: 32))
  898. make.left.equalTo(13)
  899. make.top.equalTo(17)
  900. }
  901. self.view.addSubview(heartAnimation)
  902. heartAnimation.snp.makeConstraints { make in
  903. make.size.equalTo(CGSize(width: 35, height: 30))
  904. make.right.equalTo(-10)
  905. make.centerY.equalTo(menuBtn.snp.centerY)
  906. }
  907. heartAnimation.play()
  908. self.view.addSubview(heartLabel)
  909. heartLabel.snp.makeConstraints { make in
  910. make.centerY.equalTo(heartAnimation.snp.centerY).offset(-2)
  911. make.centerX.equalTo(heartAnimation.snp.centerX)
  912. }
  913. self.view.addSubview(exchangeBtn)
  914. exchangeBtn.snp.makeConstraints { make in
  915. make.size.equalTo(CGSize(width: 34, height: 34))
  916. make.right.equalTo(heartAnimation.snp.left).offset(-10)
  917. make.centerY.equalTo(menuBtn.snp.centerY)
  918. }
  919. self.view.addSubview(teachView)
  920. teachView.snp.makeConstraints { make in
  921. make.left.right.bottom.equalTo(0)
  922. make.top.equalTo(60)
  923. }
  924. self.view.addSubview(helpView)
  925. helpView.snp.makeConstraints { make in
  926. make.left.right.bottom.equalTo(0)
  927. make.top.equalTo(60)
  928. }
  929. self.view.addSubview(prologueView)
  930. prologueView.snp.makeConstraints { make in
  931. make.left.right.bottom.equalTo(0)
  932. make.top.equalTo(60)
  933. }
  934. self.view.addSubview(chooseView)
  935. chooseView.snp.makeConstraints { make in
  936. make.width.equalTo(147)
  937. make.height.equalTo(34)
  938. make.left.equalTo(menuBtn.snp.right).offset(9)
  939. make.centerY.equalTo(menuBtn.snp.centerY)
  940. }
  941. self.view.addSubview(exchangeView)
  942. exchangeView.snp.makeConstraints { make in
  943. make.top.equalTo(menuBtn.snp.bottom)
  944. make.left.right.bottom.equalTo(0)
  945. }
  946. self.view.addSubview(menuView)
  947. menuView.snp.makeConstraints { make in
  948. make.left.right.bottom.equalTo(0)
  949. make.top.equalTo(menuBtn.snp.bottom)
  950. }
  951. self.view.addSubview(loginView)
  952. loginView.snp.makeConstraints { make in
  953. make.top.left.right.bottom.equalTo(0)
  954. }
  955. self.view.addSubview(vipView)
  956. vipView.snp.makeConstraints { make in
  957. make.top.left.right.bottom.equalTo(0)
  958. }
  959. chooseView.addSubview(functionView)
  960. functionView.snp.makeConstraints { make in
  961. make.top.left.equalTo(1)
  962. make.bottom.equalTo(-1)
  963. make.width.equalTo(85)
  964. }
  965. functionView.addSubview(functionLabel)
  966. functionLabel.snp.makeConstraints { make in
  967. make.left.equalTo(12)
  968. make.centerY.equalToSuperview()
  969. }
  970. functionView.addSubview(arrowIcon)
  971. arrowIcon.snp.makeConstraints { make in
  972. make.size.equalTo(CGSize(width: 18, height: 18))
  973. make.centerY.equalToSuperview()
  974. make.right.equalTo(-5)
  975. }
  976. chooseView.addSubview(userChangeView)
  977. userChangeView.snp.makeConstraints { make in
  978. make.left.equalTo(functionView.snp.right)
  979. make.right.equalToSuperview()
  980. make.top.bottom.equalTo(0)
  981. }
  982. userChangeView.addSubview(userChangeIcon)
  983. userChangeIcon.snp.makeConstraints { make in
  984. make.size.equalTo(CGSize(width: 12, height: 12))
  985. make.centerY.equalToSuperview()
  986. make.right.equalTo(-8)
  987. }
  988. userChangeView.addSubview(userChangeLabel)
  989. userChangeLabel.snp.makeConstraints { make in
  990. make.right.equalTo(userChangeIcon.snp.left).offset(-4)
  991. make.left.equalTo(4)
  992. make.centerY.equalToSuperview()
  993. }
  994. // chooseView.addSubview(userChangeBtn)
  995. // userChangeBtn.snp.makeConstraints { make in
  996. // make.left.equalTo(functionView.snp.right)
  997. // make.right.equalToSuperview()
  998. // make.top.bottom.equalTo(0)
  999. // }
  1000. self.nowShowView = self.helpView
  1001. }
  1002. }