KeyboardViewController.swift 40 KB

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