KeyboardViewController.swift 35 KB

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