KeyboardViewController.swift 42 KB

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