TSPTPInputVC.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. //
  2. // TSPTPInputVC.swift
  3. // AIEmoji
  4. //
  5. // Created by 100Years on 2025/4/7.
  6. //
  7. import PhotosUI
  8. class TSPTPInputVC: TSBaseVC {
  9. lazy var viewModel: TSPTPInputVM = {
  10. let viewModel = TSPTPInputVM()
  11. viewModel.isCanGennerateBlock = { [weak self] isCan in
  12. guard let self = self else { return }
  13. // submitBtn.isEnabled = isCan
  14. creatBtnView.setBtnEnabled(isEnabled: isCan)
  15. }
  16. return viewModel
  17. }()
  18. lazy var photoPickerManager: TSPhotoPickerManager = {
  19. let photoPickerManager = TSPhotoPickerManager(viewController: self)
  20. return photoPickerManager
  21. }()
  22. private var keyboardHeight: CGFloat = 0
  23. var hintBaseVC = TSAIListHintBaseVC(config: .defaultConfig)
  24. //###################################### 导航栏 view ######################################
  25. lazy var vipBtn: UIButton = {
  26. let vipBtn = UIButton.createButton(image: UIImage(named: "nav_vip")) { [weak self] in
  27. guard let self = self else { return }
  28. TSPurchaseVC.show(target: self) {}
  29. }
  30. return vipBtn
  31. }()
  32. lazy var navBarView: TSBaseNavContentBarView = {
  33. let navBarView = TSBaseNavContentBarView()
  34. let titleImageView = UIImageView.createImageView(imageName: "nav_title_pic",contentMode: .scaleToFill)
  35. navBarView.barView.addSubview(titleImageView)
  36. titleImageView.snp.makeConstraints { make in
  37. make.centerY.equalToSuperview()
  38. make.left.equalTo(16)
  39. }
  40. navBarView.barView.addSubview(vipBtn)
  41. vipBtn.snp.makeConstraints { make in
  42. make.centerY.equalToSuperview()
  43. make.trailing.equalTo(-16)//(-60)
  44. make.width.height.equalTo(24)
  45. }
  46. return navBarView
  47. }()
  48. //###################################### cusStackView ######################################
  49. lazy var cusStackView: TSCustomStackView = {
  50. let cusStackView = TSCustomStackView(axis: .vertical,spacing: 0)
  51. cusStackView.scrollView.isScrollEnabled = true
  52. return cusStackView
  53. }()
  54. //###################################### 上传图片 ######################################
  55. lazy var uploadView: TSPTPUploadView = {
  56. let uploadView = TSPTPUploadView()
  57. uploadView.clickHandel = { [weak self] index in
  58. guard let self = self else { return }
  59. if index == 0 {//删除
  60. viewModel.upLoadImage = nil
  61. uploadView.upLoadImage = nil
  62. }else{//添加
  63. if TSAIListHintBaseVC.isShowUploadImageHint{
  64. TSAIListHintBaseVC.isShowUploadImageHint = false
  65. presentModalHintVC()
  66. }else {
  67. pickSinglePhoto()
  68. }
  69. }
  70. }
  71. return uploadView
  72. }()
  73. func pickSinglePhoto() {
  74. photoPickerManager.pickCustomSinglePhoto() { [weak self] image, errorString in
  75. guard let self = self else { return }
  76. if let errorString = errorString {
  77. TSToastShared.showToast(text: errorString)
  78. }else{
  79. viewModel.upLoadImage = image
  80. uploadView.upLoadImage = image
  81. }
  82. kDelayMainShort {
  83. self.photoPickerManager.dismissPageVC()
  84. }
  85. }
  86. }
  87. //###################################### 选择风格 ######################################
  88. lazy var selectStyleView: TSPTPSelectStyleView = {
  89. let selectStyleView = TSPTPSelectStyleView()
  90. selectStyleView.currentIndexPath = IndexPath(item: viewModel.selectedStyleIndex, section: 0)
  91. selectStyleView.dataArray = viewModel.ptpStyleModels
  92. selectStyleView.clickHandle = { [weak self] model in
  93. guard let self = self else { return }
  94. viewModel.selectedPTPStyleModel = model
  95. updateVipView()
  96. updateTextFiledView()
  97. }
  98. return selectStyleView
  99. }()
  100. //###################################### 输入框 ######################################
  101. lazy var customTextView: TSPlaceholderTextView = {
  102. let customTextView = TSPlaceholderTextView(
  103. placeholder: "Describe how you want to transform".localized,
  104. text: "",
  105. font: .font(size: 14),
  106. textColor: .white,
  107. backgroundColor: .clear
  108. )
  109. customTextView.delegate = self
  110. customTextView.returnKeyType = .done
  111. return customTextView
  112. }()
  113. lazy var clearBtn: TSUIExpandedTouchButton = {
  114. let clearBtn = TSUIExpandedTouchButton()
  115. clearBtn.setUpButton(
  116. image: UIImage(named: "clear_text")
  117. )
  118. { [weak self] in
  119. guard let self = self else { return }
  120. customTextView.text = ""
  121. textViewDidChange(customTextView)
  122. }
  123. clearBtn.isHidden = true
  124. return clearBtn
  125. }()
  126. var promptTextViewH:CGFloat = 96.0
  127. lazy var promptTextView:UIView = {
  128. let promptTextView = UIView()
  129. promptTextView.isHidden = true
  130. promptTextView.clipsToBounds = true
  131. let bgView = UIView()
  132. bgView.backgroundColor = "#333333".uiColor
  133. bgView.cornerRadius = 16.0
  134. promptTextView.addSubview(bgView)
  135. bgView.snp.makeConstraints { make in
  136. make.edges.equalTo(UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16))
  137. }
  138. bgView.addSubview(customTextView)
  139. bgView.addSubview(clearBtn)
  140. customTextView.snp.makeConstraints { make in
  141. make.centerY.equalToSuperview()
  142. make.leading.equalTo(12.0)
  143. make.top.equalTo(12.0)
  144. make.bottom.equalTo(-12.0)
  145. make.trailing.equalTo(-20)
  146. }
  147. clearBtn.snp.makeConstraints { make in
  148. make.width.height.equalTo(16.0)
  149. make.trailing.equalTo(-12)
  150. make.bottom.equalTo(-12.0)
  151. }
  152. return promptTextView
  153. }()
  154. //###################################### 集合视图 ######################################
  155. private var collectionViewObserver: CollectionViewObserver!
  156. let collectionViewBtootm:CGFloat = 80
  157. lazy var collectionComponent: TSCollectionViewComponent = {
  158. let layout = UICollectionViewFlowLayout()
  159. let cp = TSCollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [:])
  160. cp.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: collectionViewBtootm, right: 0)
  161. cp.collectionView.isScrollEnabled = false
  162. // 禁用自动 contentInset 调整
  163. if #available(iOS 11.0, *) {
  164. cp.collectionView.contentInsetAdjustmentBehavior = .never
  165. } else {
  166. automaticallyAdjustsScrollViewInsets = false
  167. }
  168. cp.sectionActionHandler = { [weak self] cellCp, indexPath in
  169. guard let self = self else { return }
  170. if let cmd = cellCp as? String, cmd == "delete" {
  171. showCustomAlert(message: "Are you sure to delete".localized, deleteHandler: {
  172. self.viewModel.removeAllHistoryList()
  173. self.collectionComponent.clear()
  174. self.collectionComponent.reloadView(with: self.viewModel.colDataArray)
  175. })
  176. }
  177. }
  178. cp.itemActionHandler = { [weak self] (object, indexPath) in
  179. guard let self = self else { return }
  180. //删除过期的任务
  181. if let cmd = object as? String, cmd == "delete_task_expired" {
  182. if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSGenmojiCoLSectionModel,
  183. let currentActionInfoModel = sections.items.safeObj(At: indexPath.item) {
  184. TSRMShared.ptpDBHistory.deleteListModel(id: currentActionInfoModel.dataModel.id)
  185. self.viewModel.updateRecentData()
  186. self.collectionComponent.clear()
  187. self.collectionComponent.reloadView(with: self.viewModel.colDataArray)
  188. }
  189. }
  190. }
  191. cp.itemDidSelectedHandler = { [weak self] (object, indexPath) in
  192. guard let self = self else { return }
  193. if let sections = viewModel.colDataArray.safeObj(At: indexPath.section) as? TSGenmojiCoLSectionModel{
  194. var dataModelArray:[TSActionInfoModel] = []
  195. for itemModel in sections.items {
  196. if itemModel.dataModel.status == "success"{
  197. dataModelArray.append(itemModel.dataModel)
  198. }
  199. }
  200. let browseVC = TSAIPhotoBrowseVC()
  201. browseVC.dataModelArray = dataModelArray
  202. browseVC.currentIndex = indexPath.item
  203. kPresentModalVC(target: self, modelVC: browseVC,transitionStyle: .crossDissolve)
  204. }
  205. }
  206. cp.collectionView.keyboardDismissMode = .interactive
  207. return cp
  208. }()
  209. //###################################### 集合视图 ######################################
  210. lazy var collectionView: TSSimpleCollectionView = {
  211. let identifier = "TSGenmojiItemCell"
  212. // let itemW = (k_ScreenWidth-32.0-12.0-2.0)/2.0
  213. // let itemH = kGetScaleHeight(originalSize: CGSize(width: 165.0, height: 293.0), width: itemW)
  214. let itemW = kPTPHistoryW
  215. let itemH = kPTPHistoryH
  216. let layout = UICollectionViewFlowLayout()
  217. let cp = TSSimpleCollectionView()
  218. cp.layout.itemSize = CGSize(width: itemW, height: itemH)
  219. cp.layout.minimumLineSpacing = 12
  220. cp.layout.minimumInteritemSpacing = 12
  221. cp.collectionView.contentInset = UIEdgeInsets(top: 16, left: 16, bottom: collectionViewBtootm, right: 16)
  222. cp.registerCell(TSGenmojiItemCell.self,identifier:identifier)
  223. cp.cellIdentifierForItem = { data in
  224. return identifier
  225. }
  226. return cp
  227. }()
  228. //###################################### Button ######################################
  229. lazy var creatBtnView:TSAppBtnView = {
  230. let creatBtnView = TSAppBtnView()
  231. creatBtnView.setUpButton(style: .generate, vipFreeNumType: .picToPic) { [weak self] in
  232. guard let self = self else { return }
  233. generateImage()
  234. }
  235. creatBtnView.setBtnEnabled(isEnabled: false)
  236. creatBtnView.isIconVipBlock = { [weak self] in
  237. guard let self = self else { return false}
  238. var showVip = kPurchaseDefault.generateVipShow(type: .picToPic)
  239. if showVip == false {
  240. showVip = self.viewModel.selectedPTPStyleModel.isVip
  241. }
  242. return showVip
  243. }
  244. creatBtnView.isClickVipBlock = { [weak self] in
  245. guard let self = self else { return false}
  246. var isVip = kPurchaseDefault.freeNumAvailable(type: .picToPic) == false
  247. if viewModel.selectedPTPStyleModel.isVip == true {
  248. isVip = true
  249. }
  250. return isVip
  251. }
  252. return creatBtnView
  253. }()
  254. override func createView() {
  255. let tapGesture = UITapGestureRecognizer(target: self, action: #selector(clickView))
  256. tapGesture.cancelsTouchesInView = false
  257. view.addGestureRecognizer(tapGesture)
  258. navBarContentView.addSubview(navBarView)
  259. navBarView.snp.makeConstraints { make in
  260. make.edges.equalToSuperview()
  261. }
  262. contentView.addSubview(cusStackView)
  263. cusStackView.snp.makeConstraints { make in
  264. make.edges.equalToSuperview()
  265. }
  266. contentView.addSubview(creatBtnView)
  267. creatBtnView.snp.makeConstraints { make in
  268. make.bottom.equalTo(-16)
  269. make.leading.equalTo(16)
  270. make.trailing.equalTo(-16)
  271. make.height.equalTo(48)
  272. }
  273. setUpCusStackView()
  274. }
  275. override func dealThings() {
  276. //设置colDataArray 数据
  277. collectionComponent.clear()
  278. collectionComponent.reloadView(with:viewModel.colDataArray)
  279. //监听 vip 变化
  280. NotificationCenter.default.addObserver(self, selector: #selector(vipInfoChanged), name: .kPurchaseDidChanged, object: nil)
  281. updateVipView()
  282. TSAIListHintBaseVC.userDefaultsKey = "isFirstUploadImagePTP"
  283. // // 监听键盘事件
  284. NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
  285. NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
  286. //监听collectionView 的 contentSize
  287. collectionViewObserver = CollectionViewObserver(collectionView: collectionComponent.collectionView)
  288. collectionViewObserver.onContentSizeChange = {[weak self] size in
  289. guard let self = self else { return }
  290. print("collectionViewObserver 内容大小变化: \(size)")
  291. self.collectionComponent.collectionView.snp.updateConstraints { make in
  292. make.height.equalTo(size.height)
  293. }
  294. }
  295. //监听按钮任务生成中
  296. NotificationCenter.default.addObserver(forName: .kBaseOperationQueueCountChanged, object: nil, queue: nil) { [weak self] notification in
  297. guard let self = self else { return }
  298. setCreatBtnEnabled()
  299. }
  300. NotificationCenter.default.addObserver(forName: .kGeneratePTPOperationChanged, object: nil, queue: nil) { [weak self] notification in
  301. guard let self = self else { return }
  302. if let userInfo = notification.userInfo as? [String: Any],let state = userInfo["state"] as? TSProgressState {
  303. dePrint("TSBaseOperation stateDatauPblished 收到 = \(state)")
  304. if state.reloadNewData {
  305. self.viewModel.updateRecentData()
  306. collectionComponent.clear()
  307. collectionComponent.reloadView(with:viewModel.colDataArray)
  308. }
  309. }
  310. }
  311. }
  312. }
  313. extension TSPTPInputVC {
  314. func presentModalHintVC(){
  315. hintBaseVC = TSAIListHintBaseVC(config: .defaultConfig) { [weak self] image in
  316. guard let self = self else { return }
  317. viewModel.upLoadImage = image
  318. uploadView.upLoadImage = image
  319. hintBaseVC.dismissPageVC()
  320. }
  321. kPresentModalVC(target: self, modelVC: hintBaseVC,transitionStyle: .crossDissolve)
  322. }
  323. func setUpCusStackView(){
  324. let uploadPhotoTitleView = TSTitleView.creatTitleView(title: "Upload Photo".localized, subTitle: "")
  325. cusStackView.addSubviewToStack(uploadPhotoTitleView)
  326. uploadPhotoTitleView.snp.makeConstraints { make in
  327. make.height.equalTo(uploadPhotoTitleView.viewH)
  328. make.width.equalTo(k_ScreenWidth)
  329. }
  330. let hintBtn = TSUIExpandedTouchButton()
  331. hintBtn.setUpButton(image: UIImage(named: "ptp_hint")){ [weak self] in
  332. guard let self = self else { return }
  333. presentModalHintVC()
  334. }
  335. uploadPhotoTitleView.contentView.addSubview(hintBtn)
  336. hintBtn.snp.makeConstraints { make in
  337. make.centerY.equalToSuperview().offset(kSectionTitleViewCenterYOffset)
  338. make.trailing.equalTo(-16)
  339. make.width.height.equalTo(16)
  340. }
  341. cusStackView.addSubviewToStack(uploadView)
  342. uploadView.snp.makeConstraints { make in
  343. make.height.equalTo(uploadView.viewH)
  344. make.width.equalTo(k_ScreenWidth)
  345. }
  346. let selectStyleTitleView = TSTitleView.creatTitleView(title: "Select Style".localized)
  347. cusStackView.addSubviewToStack(selectStyleTitleView)
  348. selectStyleTitleView.snp.makeConstraints { make in
  349. make.height.equalTo(selectStyleTitleView.viewH)
  350. make.width.equalTo(k_ScreenWidth)
  351. }
  352. cusStackView.addSubviewToStack(selectStyleView)
  353. selectStyleView.snp.makeConstraints { make in
  354. make.height.equalTo(selectStyleView.viewH)
  355. make.width.equalTo(k_ScreenWidth)
  356. }
  357. cusStackView.addSubviewToStack(promptTextView,length: promptTextViewH,animate: false)
  358. promptTextView.isHidden = !viewModel.selectedPTPStyleModel.input
  359. cusStackView.addSubviewToStack(collectionComponent.collectionView)
  360. collectionComponent.collectionView.snp.makeConstraints { make in
  361. make.height.equalTo(0)
  362. make.width.equalTo(k_ScreenWidth)
  363. }
  364. cusStackView.addSpacing(length: collectionViewBtootm)
  365. }
  366. }
  367. extension TSPTPInputVC: UITextViewDelegate{
  368. // 实现 UITextViewDelegate 协议方法,控制 return 键行为
  369. func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
  370. if text == "\n" {
  371. // 当输入为换行符(即按下 return 键)时,执行相应操作
  372. //sendBolck?(textView.text)
  373. clickView()
  374. return false
  375. }
  376. return true
  377. }
  378. func textViewDidChange(_ textView: UITextView){
  379. clearBtn.isHidden = textView.text.count <= 0
  380. viewModel.selectedPTPStyleModel.inputText = textView.text
  381. viewModel.gennerateChange()
  382. }
  383. }
  384. extension TSPTPInputVC {
  385. @objc func vipInfoChanged() {
  386. kExecuteOnMainThread {
  387. self.updateVipView()
  388. }
  389. }
  390. func updateVipView() {
  391. kExecuteOnMainThread {
  392. self.vipBtn.isHidden = PurchaseManager.default.isVip
  393. self.creatBtnView.updateVipView()
  394. }
  395. }
  396. func getVipText()->String{
  397. return "Generate".localized
  398. }
  399. }
  400. extension TSPTPInputVC {
  401. @objc func clickView() {
  402. view.endEditing(true)
  403. }
  404. func generateImage() {
  405. // var isVip = kPurchaseDefault.freeNumAvailable(type: .picToPic) == false
  406. // if viewModel.selectedPTPStyleModel.isVip == true {
  407. // isVip = true
  408. // }
  409. // if kJudgeVip(externalBool: isVip, vc: self) { return } //判断 vip
  410. viewModel.selectedPTPStyleModel.upLoadImage = viewModel.upLoadImage
  411. viewModel.selectedPTPStyleModel.upLoadImageUrl = nil
  412. let gennerateVC = TSPTPGeneratorVC(generateStyleModel: viewModel.selectedPTPStyleModel) { [weak self] model in
  413. guard let self = self else { return }
  414. // if viewModel.saveModel(model:model) {
  415. // collectionComponent.clear()
  416. // collectionComponent.reloadView(with:viewModel.colDataArray)
  417. // }else{
  418. // collectionComponent.reloadData()
  419. // }
  420. updateVipView()
  421. }
  422. gennerateVC.reloadViewBlock = { [weak self] in
  423. guard let self = self else { return }
  424. collectionComponent.reloadData()
  425. }
  426. kPresentModalVC(target: self, modelVC: gennerateVC,transitionStyle: .crossDissolve)
  427. }
  428. func setCreatBtnEnabled() {
  429. let isAvailability = TSGeneratePTPOperationQueue.shared.isAvailability
  430. if viewModel.isCanGennerate,isAvailability {
  431. creatBtnView.setBtnEnabled(isEnabled: true)
  432. creatBtnView.loadingAnimation(loading: false)
  433. dePrint("TSTextGeneralPicVC setCreatBtnEnabled false")
  434. }else{
  435. dePrint("TSTextGeneralPicVC setCreatBtnEnabled = \(isAvailability)")
  436. creatBtnView.setBtnEnabled(isEnabled: false)
  437. creatBtnView.loadingAnimation(loading: !isAvailability)
  438. }
  439. }
  440. }
  441. extension TSPTPInputVC {
  442. @objc func keyboardWillShow(_ notification: Notification) {
  443. guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
  444. let scrollView = cusStackView.scrollView
  445. let keyboardHeight = keyboardFrame.height - view.safeAreaInsets.bottom
  446. let textViewFrame = scrollView.convert(customTextView.frame, from: customTextView.superview)
  447. let scrollDistance = textViewFrame.maxY - (scrollView.bounds.height - keyboardHeight)
  448. let y = scrollDistance
  449. scrollView.setContentOffset(CGPoint(x: 0, y: y),animated: true)
  450. }
  451. // MARK: - 键盘隐藏时恢复
  452. @objc private func keyboardWillHide(_ notification: Notification) {
  453. cusStackView.scrollView.contentOffset = CGPoint(x: 0, y: 0)
  454. }
  455. }
  456. extension TSPTPInputVC {
  457. func updateTextFiledView () {
  458. if viewModel.selectedPTPStyleModel.input {
  459. promptTextView.isHidden = false
  460. }else{
  461. promptTextView.isHidden = true
  462. }
  463. UIView.animate(withDuration: 0.3) {
  464. self.cusStackView.layoutIfNeeded()
  465. }
  466. }
  467. }