|
@@ -0,0 +1,281 @@
|
|
|
+//
|
|
|
+// TSAIExpandImageVC.swift
|
|
|
+// AIEmoji
|
|
|
+//
|
|
|
+// Created by 100Years on 2025/4/21.
|
|
|
+//
|
|
|
+
|
|
|
+import PhotosUI
|
|
|
+
|
|
|
+class TSAIExpandImageVC: TSBaseVC {
|
|
|
+
|
|
|
+ var upLoadImage:UIImage?{
|
|
|
+ didSet{
|
|
|
+ topImageView.image = upLoadImage
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ init(upLoadImage: UIImage) {
|
|
|
+ self.upLoadImage = upLoadImage
|
|
|
+ super.init()
|
|
|
+ }
|
|
|
+
|
|
|
+ @MainActor required init?(coder: NSCoder) {
|
|
|
+ fatalError("init(coder:) has not been implemented")
|
|
|
+ }
|
|
|
+
|
|
|
+ lazy var viewModel: TSAIExpandImageVM = {
|
|
|
+ let viewModel:TSAIExpandImageVM = TSAIExpandImageVM()
|
|
|
+ return viewModel
|
|
|
+ }()
|
|
|
+
|
|
|
+ var photoExpand = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
|
|
+
|
|
|
+ lazy var maxSize: CGSize = {
|
|
|
+ let maxSize = CGSizeMake(k_ScreenWidth, k_ScreenWidth)
|
|
|
+ return maxSize
|
|
|
+ }()
|
|
|
+
|
|
|
+ lazy var photoPickerManager: TSPhotoPickerManager = {
|
|
|
+ let photoPickerManager = TSPhotoPickerManager(viewController: self)
|
|
|
+ return photoPickerManager
|
|
|
+ }()
|
|
|
+
|
|
|
+
|
|
|
+ //###################################### topImageView ######################################
|
|
|
+ lazy var topAreaView: UIView = {
|
|
|
+ let view = UIView()
|
|
|
+
|
|
|
+ view.addSubview(expandAreaView)
|
|
|
+ expandAreaView.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ make.width.equalTo(maxSize.width)
|
|
|
+ make.height.equalTo(maxSize.height)
|
|
|
+ }
|
|
|
+
|
|
|
+ return view
|
|
|
+ }()
|
|
|
+
|
|
|
+ lazy var expandAreaView: UIView = {
|
|
|
+ let view = UIView()
|
|
|
+
|
|
|
+ let imageView = UIImageView.createImageView(imageName: "clear_bg",contentMode: .scaleToFill)
|
|
|
+ view.addSubview(imageView)
|
|
|
+ imageView.snp.makeConstraints { make in
|
|
|
+ make.edges.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ view.addSubview(topImageView)
|
|
|
+ topImageView.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ make.width.equalTo(maxSize.width)
|
|
|
+ make.height.equalTo(maxSize.height)
|
|
|
+ }
|
|
|
+
|
|
|
+ let boardView = UIView()
|
|
|
+ boardView.layer.borderColor = UIColor.white.cgColor
|
|
|
+ boardView.layer.borderWidth = 1.5
|
|
|
+ view.addSubview(boardView)
|
|
|
+ boardView.snp.makeConstraints { make in
|
|
|
+ make.edges.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ return view
|
|
|
+ }()
|
|
|
+
|
|
|
+ lazy var topImageView: UIImageView = {
|
|
|
+ let imageView = UIImageView()
|
|
|
+ imageView.isUserInteractionEnabled = true
|
|
|
+ imageView.contentMode = .scaleAspectFit
|
|
|
+ return imageView
|
|
|
+ }()
|
|
|
+ //###################################### style类型 ######################################
|
|
|
+ lazy var styleView: TSAIExpandStyleView = {
|
|
|
+ let styleView = TSAIExpandStyleView()
|
|
|
+ styleView.dataArray = viewModel.selectStyleModels
|
|
|
+ styleView.currentIndexPath = IndexPath(item: viewModel.selectedStyleIndex, section: 0)
|
|
|
+ styleView.selectedValueBlock = { [weak self] model in
|
|
|
+ guard let self = self else { return }
|
|
|
+ handelSelectedValueBlock(model: model)
|
|
|
+ }
|
|
|
+ return styleView
|
|
|
+ }()
|
|
|
+
|
|
|
+ //###################################### 提交按钮 ######################################
|
|
|
+ lazy var submitBtn: UIButton = {
|
|
|
+ let submitBtn = kCreateNormalSubmitBtn(title: "Expand".localized) { [weak self] in
|
|
|
+ guard let self = self else { return }
|
|
|
+ generateImage()
|
|
|
+ }
|
|
|
+ submitBtn.cornerRadius = 24.0
|
|
|
+ return submitBtn
|
|
|
+ }()
|
|
|
+ //###################################### createView ######################################
|
|
|
+ override func createView() {
|
|
|
+ addNormalNavBarView()
|
|
|
+ setPageTitle("AI Expand Photo".localized)
|
|
|
+ _ = setNavigationItem("", imageName: "aichat_history", direction: .right, action: #selector(clickNavRight))
|
|
|
+
|
|
|
+ contentView.addSubview(submitBtn)
|
|
|
+ contentView.addSubview(styleView)
|
|
|
+ contentView.addSubview(topAreaView)
|
|
|
+
|
|
|
+
|
|
|
+ submitBtn.snp.makeConstraints { make in
|
|
|
+ make.centerX.equalToSuperview()
|
|
|
+ make.width.equalTo(250*kDesignScale)
|
|
|
+ make.height.equalTo(48)
|
|
|
+ make.bottom.equalTo(-10-k_Height_safeAreaInsetsBottom())
|
|
|
+ }
|
|
|
+
|
|
|
+ styleView.snp.makeConstraints { make in
|
|
|
+ make.bottom.equalTo(submitBtn.snp.top).offset(-30)
|
|
|
+ make.leading.trailing.equalTo(0)
|
|
|
+ make.height.equalTo(52)
|
|
|
+ }
|
|
|
+
|
|
|
+ topAreaView.snp.makeConstraints { make in
|
|
|
+ make.top.leading.trailing.equalTo(0)
|
|
|
+ make.bottom.equalTo(styleView.snp.top).offset(0)
|
|
|
+ }
|
|
|
+
|
|
|
+ let image = upLoadImage
|
|
|
+ upLoadImage = image
|
|
|
+ updateExpandArea()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension TSAIExpandImageVC {
|
|
|
+
|
|
|
+ @objc func clickNavRight() {
|
|
|
+ kPushVC(target: self, modelVC: TSAIListHistoryBaseVC(generatorStyle: .photoExpand))
|
|
|
+ }
|
|
|
+
|
|
|
+ func handelSelectedValueBlock(model:TSGenerateStyleModel) {
|
|
|
+ if model.clickType == 2 { //换图
|
|
|
+ self.pickSinglePhoto()
|
|
|
+ }else{
|
|
|
+ viewModel.selectStyleModel = model
|
|
|
+ styleView.agreeWillSelectIndexPath()
|
|
|
+ updateExpandArea()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func pickSinglePhoto() {
|
|
|
+ photoPickerManager.pickCustomSinglePhoto() { [weak self] image, errorString in
|
|
|
+ guard let self = self else { return }
|
|
|
+ if let errorString = errorString {
|
|
|
+ TSToastShared.showToast(text: errorString)
|
|
|
+ }else{
|
|
|
+ handlePhotoPicke(image: image)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func handlePhotoPicke(image:UIImage?){
|
|
|
+ if let image = image {
|
|
|
+ upLoadImage = image
|
|
|
+ viewModel.upLoadImage = image
|
|
|
+ //选中后,去到 1:1
|
|
|
+ styleView.currentIndexPath = IndexPath(item: 1, section: 0)
|
|
|
+ viewModel.selectStyleModel = viewModel.selectStyleModels.safeObj(At: 1)
|
|
|
+ updateExpandArea()
|
|
|
+ }
|
|
|
+
|
|
|
+ if upLoadImage == nil {
|
|
|
+ pop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // MARK: - 更新扩图区域
|
|
|
+ private func updateExpandArea() {
|
|
|
+ guard let upLoadImage = upLoadImage else { return }
|
|
|
+ let imageSize = upLoadImage.size
|
|
|
+ let imageAspectRatio = imageSize.width / imageSize.height
|
|
|
+ let targetAspectRatio = viewModel.currentRatio.value
|
|
|
+
|
|
|
+ print("imageAspectRatio=\(imageAspectRatio)")
|
|
|
+ print("targetAspectRatio=\(targetAspectRatio)")
|
|
|
+ print("\n")
|
|
|
+
|
|
|
+ var expandWidth: CGFloat
|
|
|
+ var expandHeight: CGFloat
|
|
|
+
|
|
|
+ if targetAspectRatio > imageAspectRatio {
|
|
|
+ // 以宽度为基准
|
|
|
+ expandWidth = maxSize.width
|
|
|
+ expandHeight = expandWidth / targetAspectRatio
|
|
|
+ }else {
|
|
|
+ // 以高度为基准
|
|
|
+ expandHeight = maxSize.height
|
|
|
+ expandWidth = expandHeight * targetAspectRatio
|
|
|
+ }
|
|
|
+
|
|
|
+ // 确保扩图区域不超过最大限制(100%)
|
|
|
+ let maxExpandWidth = view.bounds.width
|
|
|
+ let maxExpandHeight = view.bounds.height
|
|
|
+ print("expandWidth=\(expandWidth)")
|
|
|
+ print("expandHeight=\(expandHeight)")
|
|
|
+ print("\n")
|
|
|
+ expandWidth = min(expandWidth, maxExpandWidth)
|
|
|
+ expandHeight = min(expandHeight, maxExpandHeight)
|
|
|
+
|
|
|
+ print("expandWidth=\(expandWidth)")
|
|
|
+ print("expandHeight=\(expandHeight)")
|
|
|
+ print("\n")
|
|
|
+
|
|
|
+ // 使用 SnapKit 更新约束
|
|
|
+ expandAreaView.snp.updateConstraints { make in
|
|
|
+ make.width.equalTo(expandWidth)
|
|
|
+ make.height.equalTo(expandHeight)
|
|
|
+ }
|
|
|
+
|
|
|
+ let imageRatio = min(expandWidth/imageSize.width,expandHeight/imageSize.height)
|
|
|
+ var scaleFactor = 1.0
|
|
|
+ if targetAspectRatio == 1.0,imageAspectRatio == 1.0 {
|
|
|
+ scaleFactor = 0.67
|
|
|
+ }
|
|
|
+
|
|
|
+ let imageViewW = imageSize.width * imageRatio * scaleFactor
|
|
|
+ let imageViewH = imageSize.height * imageRatio * scaleFactor
|
|
|
+ topImageView.snp.updateConstraints { make in
|
|
|
+ make.width.equalTo(imageViewW)
|
|
|
+ make.height.equalTo(imageViewH)
|
|
|
+ }
|
|
|
+
|
|
|
+ let ws = 1.0 - imageViewW/expandWidth
|
|
|
+ let hs = 1.0 - imageViewH/expandHeight
|
|
|
+
|
|
|
+ if targetAspectRatio == 1.0,imageAspectRatio == 1.0 {
|
|
|
+ photoExpand = UIEdgeInsets(top: 0.25, left: 0.25, bottom: 0.25, right: 0.25)
|
|
|
+ }else{
|
|
|
+ photoExpand = UIEdgeInsets(top: hs/2.0, left: ws/2.0, bottom: hs/2.0, right: ws/2.0)
|
|
|
+ }
|
|
|
+
|
|
|
+ print("ws=\(ws),hs=\(hs)")
|
|
|
+ print("1-ws=\(1.0-ws),1-hs=\(1.0-hs)")
|
|
|
+ print("postEdge=\(photoExpand)")
|
|
|
+
|
|
|
+ UIView.animate(withDuration: 0.3) {
|
|
|
+ self.topAreaView.layoutIfNeeded()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension TSAIExpandImageVC {
|
|
|
+
|
|
|
+ func generateImage() {
|
|
|
+ if kJudgeVip(externalBool: true, vc: self){ return } //判断 vip
|
|
|
+
|
|
|
+ guard let upLoadImage = upLoadImage else { return }
|
|
|
+ let gennerateVC = TSAIListPhotoGeneratorBaseVC(generatorModel: TSAIListPhotoGeneratorModel(upLoadImage: upLoadImage, generatorStyle: .photoExpand,expandEdge: photoExpand)) { [weak self] model in
|
|
|
+ guard let self = self else { return }
|
|
|
+ saveModel(model: model)
|
|
|
+ }
|
|
|
+
|
|
|
+ kPresentModalVC(target: self, modelVC: gennerateVC,transitionStyle: .crossDissolve)
|
|
|
+ }
|
|
|
+
|
|
|
+ func saveModel(model:TSActionInfoModel){
|
|
|
+ TSAIPhotoExpandHistory.shared.saveModel(model: model)
|
|
|
+ }
|
|
|
+}
|