diff --git a/.gitignore b/.gitignore
index f964df5c..a1e568d4 100755
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,9 @@ iOSInjectionProject/
Carthage/Build
Carthage/Checkouts
+### FLEX
+Example/Example/Classes
+
# End of https://www.gitignore.io/api/cocoapods,objective-c
Sources/.DS_Store
.DS_Store
diff --git a/Example/Example/PhotoConfigureCNViewController.swift b/Example/Example/PhotoConfigureCNViewController.swift
index 169f4d4c..30808f91 100644
--- a/Example/Example/PhotoConfigureCNViewController.swift
+++ b/Example/Example/PhotoConfigureCNViewController.swift
@@ -58,6 +58,8 @@ class PhotoConfigureCNViewController: UIViewController {
var editImageClipToolSwitch: UISwitch!
+ var editImageTextStickerToolSwitch: UISwitch!
+
var editImageMosaicToolSwitch: UISwitch!
var editImageFilterToolSwitch: UISwitch!
@@ -513,11 +515,28 @@ class PhotoConfigureCNViewController: UIViewController {
make.centerY.equalTo(clipToolLabel)
}
- // 裁剪
+ // 文本
+ let textStickerToolLabel = createLabel("文字")
+ self.editImageToolView.addSubview(textStickerToolLabel)
+ textStickerToolLabel.snp.makeConstraints { (make) in
+ make.top.equalTo(clipToolLabel.snp.bottom).offset(velSpacing)
+ make.left.equalTo(self.editImageToolView)
+ }
+
+ self.editImageTextStickerToolSwitch = UISwitch()
+ self.editImageTextStickerToolSwitch.isOn = config.editImageTools.contains(.textSticker)
+ self.editImageTextStickerToolSwitch.addTarget(self, action: #selector(textStickerToolChanged), for: .valueChanged)
+ self.editImageToolView.addSubview(self.editImageTextStickerToolSwitch)
+ self.editImageTextStickerToolSwitch.snp.makeConstraints { (make) in
+ make.left.equalTo(textStickerToolLabel.snp.right).offset(horSpacing)
+ make.centerY.equalTo(textStickerToolLabel)
+ }
+
+ // 马赛克
let mosaicToolLabel = createLabel("马赛克")
self.editImageToolView.addSubview(mosaicToolLabel)
mosaicToolLabel.snp.makeConstraints { (make) in
- make.top.equalTo(clipToolLabel.snp.bottom).offset(velSpacing)
+ make.top.equalTo(textStickerToolLabel.snp.bottom).offset(velSpacing)
make.left.equalTo(self.editImageToolView)
}
@@ -550,11 +569,10 @@ class PhotoConfigureCNViewController: UIViewController {
// 编辑视频开关
editVideoLabel = createLabel("允许编辑视频")
- editVideoLabel.tag = 1000
containerView.addSubview(editVideoLabel)
editVideoLabel.snp.makeConstraints { (make) in
if config.allowEditImage {
- make.top.equalTo(editImageLabel.snp.bottom).offset(180)
+ make.top.equalTo(editImageLabel.snp.bottom).offset(225)
} else {
make.top.equalTo(editImageLabel.snp.bottom).offset(velSpacing)
}
@@ -843,7 +861,7 @@ class PhotoConfigureCNViewController: UIViewController {
self.editImageToolView.alpha = self.config.allowEditImage ? 1 : 0
self.editVideoLabel.snp.updateConstraints({ (make) in
if self.config.allowEditImage {
- make.top.equalTo(self.editImageLabel.snp.bottom).offset(180)
+ make.top.equalTo(self.editImageLabel.snp.bottom).offset(225)
} else {
make.top.equalTo(self.editImageLabel.snp.bottom).offset(20)
}
@@ -860,6 +878,10 @@ class PhotoConfigureCNViewController: UIViewController {
config.editImageTools = ZLEditImageViewController.EditImageTool(rawValue: config.editImageTools.rawValue ^ ZLEditImageViewController.EditImageTool.clip.rawValue)
}
+ @objc func textStickerToolChanged() {
+ config.editImageTools = ZLEditImageViewController.EditImageTool(rawValue: config.editImageTools.rawValue ^ ZLEditImageViewController.EditImageTool.textSticker.rawValue)
+ }
+
@objc func mosaicToolChanged() {
config.editImageTools = ZLEditImageViewController.EditImageTool(rawValue: config.editImageTools.rawValue ^ ZLEditImageViewController.EditImageTool.mosaic.rawValue)
}
diff --git a/Example/Example/PhotoConfigureViewController.swift b/Example/Example/PhotoConfigureViewController.swift
index e7922a24..599c0b2e 100644
--- a/Example/Example/PhotoConfigureViewController.swift
+++ b/Example/Example/PhotoConfigureViewController.swift
@@ -58,6 +58,8 @@ class PhotoConfigureViewController: UIViewController {
var editImageClipToolSwitch: UISwitch!
+ var editImageTextStickerToolSwitch: UISwitch!
+
var editImageMosaicToolSwitch: UISwitch!
var editImageFilterToolSwitch: UISwitch!
@@ -513,11 +515,28 @@ class PhotoConfigureViewController: UIViewController {
make.centerY.equalTo(clipToolLabel)
}
- // 裁剪
+ // 文本
+ let textStickerToolLabel = createLabel("Text sticker")
+ self.editImageToolView.addSubview(textStickerToolLabel)
+ textStickerToolLabel.snp.makeConstraints { (make) in
+ make.top.equalTo(clipToolLabel.snp.bottom).offset(velSpacing)
+ make.left.equalTo(self.editImageToolView)
+ }
+
+ self.editImageTextStickerToolSwitch = UISwitch()
+ self.editImageTextStickerToolSwitch.isOn = config.editImageTools.contains(.textSticker)
+ self.editImageTextStickerToolSwitch.addTarget(self, action: #selector(textStickerToolChanged), for: .valueChanged)
+ self.editImageToolView.addSubview(self.editImageTextStickerToolSwitch)
+ self.editImageTextStickerToolSwitch.snp.makeConstraints { (make) in
+ make.left.equalTo(textStickerToolLabel.snp.right).offset(horSpacing)
+ make.centerY.equalTo(textStickerToolLabel)
+ }
+
+ // 马赛克
let mosaicToolLabel = createLabel("Mosaic")
self.editImageToolView.addSubview(mosaicToolLabel)
mosaicToolLabel.snp.makeConstraints { (make) in
- make.top.equalTo(clipToolLabel.snp.bottom).offset(velSpacing)
+ make.top.equalTo(textStickerToolLabel.snp.bottom).offset(velSpacing)
make.left.equalTo(self.editImageToolView)
}
@@ -550,11 +569,10 @@ class PhotoConfigureViewController: UIViewController {
// 编辑视频开关
editVideoLabel = createLabel("Edit video")
- editVideoLabel.tag = 1000
containerView.addSubview(editVideoLabel)
editVideoLabel.snp.makeConstraints { (make) in
if config.allowEditImage {
- make.top.equalTo(editImageLabel.snp.bottom).offset(180)
+ make.top.equalTo(editImageLabel.snp.bottom).offset(225)
} else {
make.top.equalTo(editImageLabel.snp.bottom).offset(velSpacing)
}
@@ -843,7 +861,7 @@ class PhotoConfigureViewController: UIViewController {
self.editImageToolView.alpha = self.config.allowEditImage ? 1 : 0
self.editVideoLabel.snp.updateConstraints({ (make) in
if self.config.allowEditImage {
- make.top.equalTo(self.editImageLabel.snp.bottom).offset(180)
+ make.top.equalTo(self.editImageLabel.snp.bottom).offset(225)
} else {
make.top.equalTo(self.editImageLabel.snp.bottom).offset(20)
}
@@ -860,6 +878,10 @@ class PhotoConfigureViewController: UIViewController {
config.editImageTools = ZLEditImageViewController.EditImageTool(rawValue: config.editImageTools.rawValue ^ ZLEditImageViewController.EditImageTool.clip.rawValue)
}
+ @objc func textStickerToolChanged() {
+ config.editImageTools = ZLEditImageViewController.EditImageTool(rawValue: config.editImageTools.rawValue ^ ZLEditImageViewController.EditImageTool.textSticker.rawValue)
+ }
+
@objc func mosaicToolChanged() {
config.editImageTools = ZLEditImageViewController.EditImageTool(rawValue: config.editImageTools.rawValue ^ ZLEditImageViewController.EditImageTool.mosaic.rawValue)
}
diff --git a/README.md b/README.md
index c02e4366..93993243 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
[](http://cocoadocs.org/docsets/ZLPhotoBrowser)
-[](https://github.com/Carthage/Carthage)
+[](https://github.com/Carthage/Carthage)
+[](https://swift.org/package-manager/)
[](http://cocoadocs.org/docsets/ZLPhotoBrowser)
[](http://cocoadocs.org/docsets/ZLPhotoBrowser)

@@ -34,7 +35,7 @@ Detailed usage of `Swift` and `OC`, please refer to [Wiki](https://github.com/lo
- [x] Image/Gif/LivePhoto/Video Support.
- [x] Customize the maximum number of previews or selection, the maximum and minimum optional duration of the video.
- [x] Customize the number of columns displayed in each row.
-- [x] Image editor (Draw/Crop/Mosaic/Filter), (Draw color can be customized; Crop ratio can be customized; Filter effect can be customized; You can choose the editing tool you want).
+- [x] Image editor (Draw/Crop/Text sticker/Mosaic/Filter), (Draw color can be customized; Crop ratio can be customized; Filter effect can be customized; You can choose the editing tool you want).
- [x] Video editor.
- [x] Custom camera.
- [x] Multi-language.
@@ -46,7 +47,7 @@ Detailed usage of `Swift` and `OC`, please refer to [Wiki](https://github.com/lo
- [x] The color of each part of the framework can be customized (Provide dynamic color can support light/dark mode).
- [x] Customize images.
-> If you have good needs and suggestions in use, or encounter any bugs, please feel free to issue and I will reply in time
+> If you have good needs and suggestions in use, or encounter any bugs, please create an issue and I will reply in time.
### Requirements
* iOS 10.0
@@ -94,6 +95,7 @@ Detailed usage of `Swift` and `OC`, please refer to [Wiki](https://github.com/lo
Support iOS14 limited authority.
Provides the ability to preview PHAsset, local images and videos, network images and videos together.
Optimize some UI effects.
+ Support show image crop vc directly.
● 4.0.8:
Add filter to image editor.
● 4.0.7:
@@ -152,7 +154,7 @@ $ carthage update ZLPhotoBrowser
#### Swift Package Manager
1. Select File > Swift Packages > Add Package Dependency. Enter https://github.com/longitachi/ZLPhotoBrowser.git in the "Choose Package Repository" dialog.
-2. In the next page, specify the version resolving rule as "Up to Next Major" with "4.0.8" as its earliest version.
+2. In the next page, specify the version resolving rule as "Up to Next Major" with "4.0.9" as its earliest version.
3. After Xcode checking out the source and resolving the version, you can choose the "ZLPhotoBrowser" library and add it to your app target.
### Demo Effect
diff --git a/README_CN.md b/README_CN.md
index 30f27f43..37496db7 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -1,5 +1,6 @@
[](http://cocoadocs.org/docsets/ZLPhotoBrowser)
-[](https://github.com/Carthage/Carthage)
+[](https://github.com/Carthage/Carthage)
+[](https://swift.org/package-manager/)
[](http://cocoadocs.org/docsets/ZLPhotoBrowser)
[](http://cocoadocs.org/docsets/ZLPhotoBrowser)

@@ -38,7 +39,7 @@
- [x] 图片/Gif/LivePhoto/Video 混合选择
- [x] 自定义最大预览数/选择数/视频最大最小可选时长,控制可否选择原图
- [x] 自定义每行显示列数
-- [x] 图片编辑(涂鸦/裁剪/马赛克/滤镜)(图片编辑可编辑多张;涂鸦颜色可自定义;裁剪比例可自定义;滤镜效果可自定义;编辑工具可根据需要自行选择)
+- [x] 图片编辑(涂鸦/裁剪/添加文字/马赛克/滤镜)(图片编辑可编辑多张;涂鸦/文字颜色可自定义;裁剪比例可自定义;滤镜效果可自定义;编辑工具可根据需要自行选择)
- [x] 视频编辑(自定义最大裁剪时长)(效果参照微信视频编辑;支持编辑本地视频)
- [x] 自定义相机(效果参照微信拍照,点击拍照、长按拍摄;上滑调整焦距;可设置最大/最小录制时间及视频分辨率;可设置闪光灯模式及视频导出格式;可根据自己需要控制是否使用自定义相机)
- [x] 多语言国际化支持(中文简/繁,英文,日文,开发者可选根据系统或自己指定,多语言文案可自定义)
@@ -96,7 +97,7 @@
### 更新日志
> [更多更新日志](https://github.com/longitachi/ZLPhotoBrowser/blob/master/UPDATELOG.md)
```
-● 4.0.9: 支持更多国家的语言; 完善iOS14 limited authority 权限的适配; 提供可以同时预览PHAsset、本地图片/视频及网络图片/视频的功能; 优化部分UI效果;
+● 4.0.9: 支持更多国家的语言; 完善iOS14 limited authority 权限的适配; 提供可以同时预览PHAsset、本地图片/视频及网络图片/视频的功能; 优化部分UI效果; 编辑图片可直接跳转裁剪界面;
● 4.0.8: 编辑图片添加滤镜功能;
● 4.0.7: 支持设置图片裁剪比例; 自定义相机支持录制时切换摄像头; 新增已选照片边框功能; 新增是否允许预览大图功能;其他细节优化及已知bug修复;
● 4.0.5: 适配iOS14 limited权限; 优化图片预览显示; 优化大长/宽图编辑;
@@ -122,7 +123,7 @@
* Swift Package Manager (该方式集成暂时有问题,图片及多语言资源无法读取,请暂时先用其他方式)
* 1. 选择 File > Swift Packages > Add Package Dependency,输入 `https://github.com/longitachi/ZLPhotoBrowser.git`
- * 2. 输入对应版本号(SPM 最低版本为 `4.0.5`)
+ * 2. 输入对应版本号(SPM 最低版本为 `4.0.9`)
* 3. 等Xcode下载完成后确定即可
### 效果图
diff --git a/Sources/Edit/ZLClipImageViewController.swift b/Sources/Edit/ZLClipImageViewController.swift
index 6d4a4959..1646d307 100644
--- a/Sources/Edit/ZLClipImageViewController.swift
+++ b/Sources/Edit/ZLClipImageViewController.swift
@@ -817,7 +817,6 @@ class ZLClipImageViewController: UIViewController {
self.resetTimer = nil
}
-
func moveClipContentToCenter() {
let maxClipRect = self.maxClipFrame
var clipRect = self.clipBoxFrame
diff --git a/Sources/Edit/ZLEditImageViewController.swift b/Sources/Edit/ZLEditImageViewController.swift
index eb583ffb..cdb4d867 100644
--- a/Sources/Edit/ZLEditImageViewController.swift
+++ b/Sources/Edit/ZLEditImageViewController.swift
@@ -40,13 +40,16 @@ public class ZLEditImageModel: NSObject {
public let selectFilter: ZLFilter?
- init(drawPaths: [ZLDrawPath], mosaicPaths: [ZLMosaicPath], editRect: CGRect?, angle: CGFloat, selectRatio: ZLImageClipRatio?, selectFilter: ZLFilter) {
+ public let textStickers: [ZLTextStickerState]?
+
+ init(drawPaths: [ZLDrawPath], mosaicPaths: [ZLMosaicPath], editRect: CGRect?, angle: CGFloat, selectRatio: ZLImageClipRatio?, selectFilter: ZLFilter, textStickers: [ZLTextStickerState]?) {
self.drawPaths = drawPaths
self.mosaicPaths = mosaicPaths
self.editRect = editRect
self.angle = angle
self.selectRatio = selectRatio
self.selectFilter = selectFilter
+ self.textStickers = textStickers
super.init()
}
@@ -58,6 +61,8 @@ public class ZLEditImageViewController: UIViewController {
static let maxDrawLineImageWidth: CGFloat = 600
+ static let ashbinNormalBgColor = zlRGB(40, 40, 40).withAlphaComponent(0.8)
+
var originalImage: UIImage
// 第一次进入界面时,布局后frame,裁剪dimiss动画使用
@@ -66,7 +71,7 @@ public class ZLEditImageViewController: UIViewController {
// 图片可编辑rect
var editRect: CGRect
- let tools: ZLEditImageViewController.EditImageTool
+ let tools: [ZLEditImageViewController.EditImageTool]
var selectRatio: ZLImageClipRatio?
@@ -78,12 +83,15 @@ public class ZLEditImageViewController: UIViewController {
var containerView: UIView!
- // 显示图片
+ // Show image.
var imageView: UIImageView!
- // 显示涂鸦
+ // Show draw lines.
var drawingImageView: UIImageView!
+ // Show text stickers.
+ var textStickersContainer: UIView!
+
// 处理好的马赛克图片
var mosaicImage: UIImage?
@@ -103,33 +111,31 @@ public class ZLEditImageViewController: UIViewController {
var bottomShadowLayer: CAGradientLayer!
- var drawBtn: UIButton!
-
- var clipBtn: UIButton!
-
- var mosaicBtn: UIButton!
-
- var filterBtn: UIButton!
-
var doneBtn: UIButton!
var revokeBtn: UIButton!
- // 选择涂鸦颜色
+ var selectedTool: ZLEditImageViewController.EditImageTool?
+
+ var editToolCollectionView: UICollectionView!
+
var drawColorCollectionView: UICollectionView!
- // 滤镜
var filterCollectionView: UICollectionView!
+ var ashbinView: UIView!
+
+ var ashbinImgView: UIImageView!
+
let drawColors: [UIColor]
var currentDrawColor = ZLPhotoConfiguration.default().editImageDefaultDrawColor
- var drawPaths: [ZLDrawPath] = []
+ var drawPaths: [ZLDrawPath]
var drawLineWidth: CGFloat = 5
- var mosaicPaths: [ZLMosaicPath] = []
+ var mosaicPaths: [ZLMosaicPath]
var mosaicLineWidth: CGFloat = 25
@@ -141,11 +147,15 @@ public class ZLEditImageViewController: UIViewController {
var currentFilter: ZLFilter
+ var textStickers: [ZLTextStickerView] = []
+
var isScrolling = false
var shouldLayout = true
- var angle: CGFloat = 0
+ var angle: CGFloat
+
+ var panGes: UIPanGestureRecognizer!
var imageSize: CGSize {
if self.angle == -90 || self.angle == -270 {
@@ -172,7 +182,7 @@ public class ZLEditImageViewController: UIViewController {
if ZLPhotoConfiguration.default().showClipDirectlyIfOnlyHasClipTool, ZLPhotoConfiguration.default().editImageTools.rawValue == ZLEditImageViewController.EditImageTool.clip.rawValue {
let vc = ZLClipImageViewController(image: image, editRect: editModel?.editRect, angle: editModel?.angle ?? 0, selectRatio: editModel?.selectRatio)
vc.clipDoneBlock = { (angle, editRect, ratio) in
- let m = ZLEditImageModel(drawPaths: [], mosaicPaths: [], editRect: editRect, angle: angle, selectRatio: ratio, selectFilter: .normal)
+ let m = ZLEditImageModel(drawPaths: [], mosaicPaths: [], editRect: editRect, angle: angle, selectRatio: ratio, selectFilter: .normal, textStickers: nil)
completion?(image.clipImage(angle, editRect) ?? image, m)
}
vc.modalPresentationStyle = .fullScreen
@@ -190,19 +200,40 @@ public class ZLEditImageViewController: UIViewController {
@objc public init(image: UIImage, editModel: ZLEditImageModel? = nil) {
self.originalImage = image
self.editImage = image
+ self.editRect = editModel?.editRect ?? CGRect(origin: .zero, size: image.size)
+ self.drawColors = ZLPhotoConfiguration.default().editImageDrawColors
+ self.currentFilter = editModel?.selectFilter ?? .normal
self.drawPaths = editModel?.drawPaths ?? []
self.mosaicPaths = editModel?.mosaicPaths ?? []
self.angle = editModel?.angle ?? 0
- self.tools = ZLPhotoConfiguration.default().editImageTools
self.selectRatio = editModel?.selectRatio
- self.editRect = editModel?.editRect ?? CGRect(origin: .zero, size: image.size)
- self.drawColors = ZLPhotoConfiguration.default().editImageDrawColors
- self.currentFilter = editModel?.selectFilter ?? .normal
+
+ var tools: [ZLEditImageViewController.EditImageTool] = []
+ if ZLPhotoConfiguration.default().editImageTools.contains(.draw) {
+ tools.append(.draw)
+ }
+ if ZLPhotoConfiguration.default().editImageTools.contains(.clip) {
+ tools.append(.clip)
+ }
+ if ZLPhotoConfiguration.default().editImageTools.contains(.textSticker) {
+ tools.append(.textSticker)
+ }
+ if ZLPhotoConfiguration.default().editImageTools.contains(.mosaic) {
+ tools.append(.mosaic)
+ }
+ if ZLPhotoConfiguration.default().editImageTools.contains(.filter) {
+ tools.append(.filter)
+ }
+ self.tools = tools
+
super.init(nibName: nil, bundle: nil)
if !self.drawColors.contains(self.currentDrawColor) {
self.currentDrawColor = self.drawColors.first!
}
+ if let ts = editModel?.textStickers {
+ self.textStickers = ts.map { ZLTextStickerView(from: $0) }
+ }
}
required init?(coder: NSCoder) {
@@ -247,31 +278,13 @@ public class ZLEditImageViewController: UIViewController {
self.filterCollectionView.frame = CGRect(x: 30, y: 0, width: self.view.frame.width-60, height: ZLEditImageViewController.filterColViewH)
- var toolBtnX: CGFloat = 30
- let toolBtnY: CGFloat = 85
- let toolBtnSize = CGSize(width: 30, height: 30)
- let toolBtnSpacing: CGFloat = 25
- if self.tools.contains(.draw) {
- self.drawBtn.frame = CGRect(origin: CGPoint(x: toolBtnX, y: toolBtnY), size: toolBtnSize)
- toolBtnX += toolBtnSize.width + toolBtnSpacing
- }
- if self.tools.contains(.clip) {
- self.clipBtn.frame = CGRect(origin: CGPoint(x: toolBtnX, y: toolBtnY), size: toolBtnSize)
- toolBtnX += toolBtnSize.width + toolBtnSpacing
- }
- if self.tools.contains(.mosaic) {
- self.mosaicBtn.frame = CGRect(origin: CGPoint(x: toolBtnX, y: toolBtnY), size: toolBtnSize)
- toolBtnX += toolBtnSize.width + toolBtnSpacing
- }
- if self.tools.contains(.filter) {
- self.filterBtn.frame = CGRect(origin: CGPoint(x: toolBtnX, y: toolBtnY), size: toolBtnSize)
- toolBtnX += toolBtnSize.width + toolBtnSpacing
- }
+ let toolY: CGFloat = 85
let doneBtnH = ZLLayout.bottomToolBtnH
let doneBtnW = localLanguageTextValue(.editFinish).boundingRect(font: ZLLayout.bottomToolTitleFont, limitSize: CGSize(width: CGFloat.greatestFiniteMagnitude, height: doneBtnH)).width + 20
- // y 多减的 8 是为了和工具条居中 (50 - doneBtnH) / 2 = 8
- self.doneBtn.frame = CGRect(x: self.view.frame.width-15-doneBtnW, y: toolBtnY, width: doneBtnW, height: doneBtnH)
+ self.doneBtn.frame = CGRect(x: self.view.frame.width-15-doneBtnW, y: toolY-2, width: doneBtnW, height: doneBtnH)
+
+ self.editToolCollectionView.frame = CGRect(x: 30, y: toolY, width: self.view.bounds.width - 30 - 30 - doneBtnW, height: 30)
if !self.drawPaths.isEmpty {
self.drawLine()
@@ -318,7 +331,7 @@ public class ZLEditImageViewController: UIViewController {
self.imageView.image = self.editImage
let editSize = self.editRect.size
- let scrollViewSize = self.scrollView.frame
+ let scrollViewSize = self.scrollView.frame.size
let ratio = min(scrollViewSize.width / editSize.width, scrollViewSize.height / editSize.height)
let w = ratio * editSize.width * self.scrollView.zoomScale
let h = ratio * editSize.height * self.scrollView.zoomScale
@@ -330,6 +343,7 @@ public class ZLEditImageViewController: UIViewController {
self.mosaicImageLayer?.frame = self.imageView.bounds
self.mosaicImageLayerMaskLayer?.frame = self.imageView.bounds
self.drawingImageView.frame = self.imageView.frame
+ self.textStickersContainer.frame = self.imageView.frame
// 针对于长图的优化
if (self.editRect.height / self.editRect.width) > (self.view.frame.height / self.view.frame.width * 1.1) {
@@ -370,6 +384,9 @@ public class ZLEditImageViewController: UIViewController {
self.drawingImageView.isUserInteractionEnabled = true
self.containerView.addSubview(self.drawingImageView)
+ self.textStickersContainer = UIView()
+ self.containerView.addSubview(self.textStickersContainer)
+
let color1 = UIColor.black.withAlphaComponent(0.35).cgColor
let color2 = UIColor.black.withAlphaComponent(0).cgColor
self.topShadowView = UIView()
@@ -395,32 +412,19 @@ public class ZLEditImageViewController: UIViewController {
self.bottomShadowLayer.locations = [0, 1]
self.bottomShadowView.layer.addSublayer(self.bottomShadowLayer)
- self.drawBtn = UIButton(type: .custom)
- self.drawBtn.setImage(getImage("zl_drawLine"), for: .normal)
- self.drawBtn.setImage(getImage("zl_drawLine_selected"), for: .selected)
- self.drawBtn.adjustsImageWhenHighlighted = false
- self.drawBtn.addTarget(self, action: #selector(drawBtnClick), for: .touchUpInside)
- self.bottomShadowView.addSubview(self.drawBtn)
-
- self.clipBtn = UIButton(type: .custom)
- self.clipBtn.setImage(getImage("zl_clip"), for: .normal)
- self.clipBtn.adjustsImageWhenHighlighted = false
- self.clipBtn.addTarget(self, action: #selector(clipBtnClick), for: .touchUpInside)
- self.bottomShadowView.addSubview(self.clipBtn)
-
- self.mosaicBtn = UIButton(type: .custom)
- self.mosaicBtn.setImage(getImage("zl_mosaic"), for: .normal)
- self.mosaicBtn.setImage(getImage("zl_mosaic_selected"), for: .selected)
- self.mosaicBtn.adjustsImageWhenHighlighted = false
- self.mosaicBtn.addTarget(self, action: #selector(mosaicBtnClick), for: .touchUpInside)
- self.bottomShadowView.addSubview(self.mosaicBtn)
-
- self.filterBtn = UIButton(type: .custom)
- self.filterBtn.setImage(getImage("zl_filter"), for: .normal)
- self.filterBtn.setImage(getImage("zl_filter_selected"), for: .selected)
- self.filterBtn.adjustsImageWhenHighlighted = false
- self.filterBtn.addTarget(self, action: #selector(filterBtnClick), for: .touchUpInside)
- self.bottomShadowView.addSubview(self.filterBtn)
+ let editToolLayout = UICollectionViewFlowLayout()
+ editToolLayout.itemSize = CGSize(width: 30, height: 30)
+ editToolLayout.minimumLineSpacing = 25
+ editToolLayout.minimumInteritemSpacing = 25
+ editToolLayout.scrollDirection = .horizontal
+ self.editToolCollectionView = UICollectionView(frame: .zero, collectionViewLayout: editToolLayout)
+ self.editToolCollectionView.backgroundColor = .clear
+ self.editToolCollectionView.delegate = self
+ self.editToolCollectionView.dataSource = self
+ self.editToolCollectionView.showsHorizontalScrollIndicator = false
+ self.bottomShadowView.addSubview(self.editToolCollectionView)
+
+ ZLEditToolCell.zl_register(self.editToolCollectionView)
self.doneBtn = UIButton(type: .custom)
self.doneBtn.titleLabel?.font = ZLLayout.bottomToolTitleFont
@@ -431,13 +435,13 @@ public class ZLEditImageViewController: UIViewController {
self.doneBtn.layer.cornerRadius = ZLLayout.bottomToolBtnCornerRadius
self.bottomShadowView.addSubview(self.doneBtn)
- let layout = UICollectionViewFlowLayout()
- layout.itemSize = CGSize(width: 30, height: 30)
- layout.minimumLineSpacing = 15
- layout.minimumInteritemSpacing = 15
- layout.scrollDirection = .horizontal
- layout.sectionInset = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)
- self.drawColorCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+ let drawColorLayout = UICollectionViewFlowLayout()
+ drawColorLayout.itemSize = CGSize(width: 30, height: 30)
+ drawColorLayout.minimumLineSpacing = 15
+ drawColorLayout.minimumInteritemSpacing = 15
+ drawColorLayout.scrollDirection = .horizontal
+ drawColorLayout.sectionInset = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)
+ self.drawColorCollectionView = UICollectionView(frame: .zero, collectionViewLayout: drawColorLayout)
self.drawColorCollectionView.backgroundColor = .clear
self.drawColorCollectionView.delegate = self
self.drawColorCollectionView.dataSource = self
@@ -471,6 +475,27 @@ public class ZLEditImageViewController: UIViewController {
self.revokeBtn.addTarget(self, action: #selector(revokeBtnClick), for: .touchUpInside)
self.bottomShadowView.addSubview(self.revokeBtn)
+ let ashbinSize = CGSize(width: 170, height: 80)
+ self.ashbinView = UIView(frame: CGRect(x: (self.view.frame.width-ashbinSize.width)/2, y: self.view.frame.height-ashbinSize.height-15, width: ashbinSize.width, height: ashbinSize.height))
+ self.ashbinView.backgroundColor = ZLEditImageViewController.ashbinNormalBgColor
+ self.ashbinView.layer.cornerRadius = 10
+ self.ashbinView.layer.masksToBounds = true
+ self.ashbinView.alpha = 0
+ self.view.addSubview(self.ashbinView)
+
+ self.ashbinImgView = UIImageView(image: getImage("zl_ashbin"), highlightedImage: getImage("zl_ashbin_open"))
+ self.ashbinImgView.frame = CGRect(x: (ashbinSize.width-25)/2, y: 15, width: 25, height: 25)
+ self.ashbinView.addSubview(self.ashbinImgView)
+
+ let asbinTipLabel = UILabel(frame: CGRect(x: 0, y: ashbinSize.height-34, width: ashbinSize.width, height: 34))
+ asbinTipLabel.font = getFont(12)
+ asbinTipLabel.textAlignment = .center
+ asbinTipLabel.textColor = .white
+ asbinTipLabel.text = localLanguageTextValue(.textStickerRemoveTips)
+ asbinTipLabel.numberOfLines = 2
+ asbinTipLabel.lineBreakMode = .byCharWrapping
+ self.ashbinView.addSubview(asbinTipLabel)
+
if self.tools.contains(.mosaic) {
// 之前选择过滤镜
if let applier = self.currentFilter.applier {
@@ -497,45 +522,48 @@ public class ZLEditImageViewController: UIViewController {
self.mosaicImageLayer?.mask = self.mosaicImageLayerMaskLayer
}
- let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction(_:)))
- tap.delegate = self
- self.view.addGestureRecognizer(tap)
+ let tapGes = UITapGestureRecognizer(target: self, action: #selector(tapAction(_:)))
+ tapGes.delegate = self
+ self.view.addGestureRecognizer(tapGes)
+
+ self.panGes = UIPanGestureRecognizer(target: self, action: #selector(drawAction(_:)))
+ self.panGes.maximumNumberOfTouches = 1
+ self.panGes.delegate = self
+ self.view.addGestureRecognizer(self.panGes)
+ self.scrollView.panGestureRecognizer.require(toFail: self.panGes)
- let pan = UIPanGestureRecognizer(target: self, action: #selector(drawAction(_:)))
- pan.maximumNumberOfTouches = 1
- pan.delegate = self
- self.view.addGestureRecognizer(pan)
- self.scrollView.panGestureRecognizer.require(toFail: pan)
+ self.textStickers.forEach { (tv) in
+ self.textStickersContainer.addSubview(tv)
+ tv.frame = tv.originFrame
+ self.configTextSticker(tv)
+ }
}
func rotationImageView() {
- var transform = CGAffineTransform.identity
- if self.angle == -90 {
- transform = CGAffineTransform(rotationAngle: -CGFloat.pi/2)
- } else if self.angle == -180 {
- transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
- } else if self.angle == -270 {
- transform = CGAffineTransform(rotationAngle: -CGFloat.pi*3/2)
- }
+ let transform = CGAffineTransform(rotationAngle: self.angle.toPi)
self.imageView.transform = transform
self.drawingImageView.transform = transform
+ self.textStickersContainer.transform = transform
}
@objc func cancelBtnClick() {
self.dismiss(animated: false, completion: nil)
}
- @objc func drawBtnClick() {
- self.drawBtn.isSelected = !self.drawBtn.isSelected
- self.drawColorCollectionView.isHidden = !self.drawBtn.isSelected
- self.revokeBtn.isHidden = !self.drawBtn.isSelected
+ func drawBtnClick() {
+ let isSelected = self.selectedTool != .draw
+ if isSelected {
+ self.selectedTool = .draw
+ } else {
+ self.selectedTool = nil
+ }
+ self.drawColorCollectionView.isHidden = !isSelected
+ self.revokeBtn.isHidden = !isSelected
self.revokeBtn.isEnabled = self.drawPaths.count > 0
- self.mosaicBtn.isSelected = false
- self.filterBtn.isSelected = false
self.filterCollectionView.isHidden = true
}
- @objc func clipBtnClick() {
+ func clipBtnClick() {
let currentEditImage = self.buildImage()
// 这里要传store_editRect,因为第一次进入编辑界面时候需要编辑界面根据这个判断是不是第一次进入
let vc = ZLClipImageViewController(image: currentEditImage, editRect: self.editRect, angle: self.angle, selectRatio: self.selectRatio)
@@ -546,6 +574,8 @@ public class ZLEditImageViewController: UIViewController {
vc.clipDoneBlock = { [weak self] (angle, editFrame, selectRatio) in
guard let `self` = self else { return }
+ let oldAngle = self.angle
+ let oldContainerSize = self.textStickersContainer.frame.size
if self.angle != angle {
self.angle = angle
self.rotationImageView()
@@ -553,6 +583,7 @@ public class ZLEditImageViewController: UIViewController {
self.editRect = editFrame
self.selectRatio = selectRatio
self.resetContainerViewFrame()
+ self.reCalculateTextStickersFrame(oldContainerSize, oldAngle, angle)
}
vc.cancelClipBlock = { [weak self] () in
@@ -566,41 +597,62 @@ public class ZLEditImageViewController: UIViewController {
}
}
- @objc func mosaicBtnClick() {
- self.drawBtn.isSelected = false
+ func textStickerBtnClick() {
+ self.showInputTextVC { [weak self] (text, textColor, bgColor) in
+ self?.addTextStickersView(text, textColor: textColor, bgColor: bgColor)
+ }
+ }
+
+ func mosaicBtnClick() {
+ let isSelected = self.selectedTool != .mosaic
+ if isSelected {
+ self.selectedTool = .mosaic
+ } else {
+ self.selectedTool = nil
+ }
+
self.drawColorCollectionView.isHidden = true
- self.filterBtn.isSelected = false
self.filterCollectionView.isHidden = true
- self.mosaicBtn.isSelected = !self.mosaicBtn.isSelected
- self.revokeBtn.isHidden = !self.mosaicBtn.isSelected
+ self.revokeBtn.isHidden = !isSelected
self.revokeBtn.isEnabled = self.mosaicPaths.count > 0
}
- @objc func filterBtnClick() {
- self.drawBtn.isSelected = false
+ func filterBtnClick() {
+ let isSelected = self.selectedTool != .filter
+ if isSelected {
+ self.selectedTool = .filter
+ } else {
+ self.selectedTool = nil
+ }
+
self.drawColorCollectionView.isHidden = true
self.revokeBtn.isHidden = true
- self.mosaicBtn.isSelected = false
- self.filterBtn.isSelected = !self.filterBtn.isSelected
- self.filterCollectionView.isHidden = !self.filterBtn.isSelected
+ self.filterCollectionView.isHidden = !isSelected
}
@objc func doneBtnClick() {
+ let textStickers = self.textStickersContainer.subviews.compactMap { (view) -> ZLTextStickerState? in
+ if let ts = view as? ZLTextStickerView, let _ = ts.label.text {
+ return ts.state
+ } else {
+ return nil
+ }
+ }
var image = self.buildImage()
image = image.clipImage(self.angle, self.editRect) ?? image
- self.editFinishBlock?(image, ZLEditImageModel(drawPaths: self.drawPaths, mosaicPaths: self.mosaicPaths, editRect: self.editRect, angle: self.angle, selectRatio: self.selectRatio, selectFilter: self.currentFilter))
+ self.editFinishBlock?(image, ZLEditImageModel(drawPaths: self.drawPaths, mosaicPaths: self.mosaicPaths, editRect: self.editRect, angle: self.angle, selectRatio: self.selectRatio, selectFilter: self.currentFilter, textStickers: textStickers))
self.dismiss(animated: false, completion: nil)
}
@objc func revokeBtnClick() {
- if self.drawBtn.isSelected {
+ if self.selectedTool == .draw {
guard !self.drawPaths.isEmpty else {
return
}
self.drawPaths.removeLast()
self.revokeBtn.isEnabled = self.drawPaths.count > 0
self.drawLine()
- } else if self.mosaicBtn.isSelected {
+ } else if self.selectedTool == .mosaic {
guard !self.mosaicPaths.isEmpty else {
return
}
@@ -619,7 +671,7 @@ public class ZLEditImageViewController: UIViewController {
}
@objc func drawAction(_ pan: UIPanGestureRecognizer) {
- if self.drawBtn.isSelected {
+ if self.selectedTool == .draw {
let point = pan.location(in: self.drawingImageView)
if pan.state == .began {
self.setToolView(show: false)
@@ -650,7 +702,7 @@ public class ZLEditImageViewController: UIViewController {
self.setToolView(show: true)
self.revokeBtn.isEnabled = self.drawPaths.count > 0
}
- } else if self.mosaicBtn.isSelected {
+ } else if self.selectedTool == .mosaic {
let point = pan.location(in: self.imageView)
if pan.state == .began {
self.setToolView(show: false)
@@ -695,6 +747,70 @@ public class ZLEditImageViewController: UIViewController {
}
}
+ func showInputTextVC(_ text: String? = nil, textColor: UIColor? = nil, bgColor: UIColor? = nil, completion: @escaping ( (String, UIColor, UIColor) -> Void )) {
+ // Calculate image displayed frame on the screen.
+ var r = self.scrollView.convert(self.view.frame, to: self.containerView)
+ r.origin.x += self.scrollView.contentOffset.x / self.scrollView.zoomScale
+ r.origin.y += self.scrollView.contentOffset.y / self.scrollView.zoomScale
+ let scale = self.imageSize.width / self.imageView.frame.width
+ r.origin.x *= scale
+ r.origin.y *= scale
+ r.size.width *= scale
+ r.size.height *= scale
+ let bgImage = self.buildImage().clipImage(self.angle, self.editRect)?.clipImage(0, r)
+ let vc = ZLInputTextViewController(image: bgImage, text: text, textColor: textColor, bgColor: bgColor)
+
+ vc.endInput = { (text, textColor, bgColor) in
+ completion(text, textColor, bgColor)
+ }
+
+ vc.modalPresentationStyle = .fullScreen
+ self.showDetailViewController(vc, sender: nil)
+ }
+
+ /// Text sticker
+ func addTextStickersView(_ text: String, textColor: UIColor, bgColor: UIColor) {
+ guard !text.isEmpty else { return }
+ let scale = self.scrollView.zoomScale
+ let size = ZLTextStickerView.calculateSize(text: text, width: self.view.frame.width)
+
+ // Calculate the display rect of container view.
+ let x = (self.scrollView.contentOffset.x - self.containerView.frame.minX) / scale
+ let y = (self.scrollView.contentOffset.y - self.containerView.frame.minY) / scale
+ let w = view.frame.width / scale
+ let h = view.frame.height / scale
+ // Convert to text stickers container view.
+ let r = self.containerView.convert(CGRect(x: x, y: y, width: w, height: h), to: self.textStickersContainer)
+ let originFrame = CGRect(x: r.minX + (r.width - size.width) / 2, y: r.minY + (r.height - size.height) / 2, width: size.width, height: size.height)
+
+ let textSticker = ZLTextStickerView(text: text, textColor: textColor, bgColor: bgColor, zoomScale: 1 / scale, originAngle: -self.angle, originFrame: originFrame)
+ self.textStickersContainer.addSubview(textSticker)
+ textSticker.frame = originFrame
+
+ self.configTextSticker(textSticker)
+ }
+
+ func configTextSticker(_ textSticker: ZLTextStickerView) {
+ textSticker.delegate = self
+ self.scrollView.pinchGestureRecognizer?.require(toFail: textSticker.pinchGes)
+ self.scrollView.panGestureRecognizer.require(toFail: textSticker.panGes)
+ self.panGes.require(toFail: textSticker.panGes)
+ }
+
+ func reCalculateTextStickersFrame(_ oldSize: CGSize, _ oldAngle: CGFloat, _ newAngle: CGFloat) {
+ let currSize = self.textStickersContainer.frame.size
+ let scale: CGFloat
+ if Int(newAngle - oldAngle) % 180 == 0{
+ scale = currSize.width / oldSize.width
+ } else {
+ scale = currSize.height / oldSize.width
+ }
+
+ self.textStickersContainer.subviews.forEach { (view) in
+ (view as? ZLTextStickerView)?.addScale(scale)
+ }
+ }
+
func drawLine() {
let originalRatio = min(self.scrollView.frame.width / self.originalImage.size.width, self.scrollView.frame.height / self.originalImage.size.height)
let ratio = min(self.scrollView.frame.width / self.editRect.width, self.scrollView.frame.height / self.editRect.height)
@@ -779,6 +895,16 @@ public class ZLEditImageViewController: UIViewController {
self.drawingImageView.image?.draw(in: CGRect(origin: .zero, size: imageSize))
+ if !self.textStickersContainer.subviews.isEmpty, let context = UIGraphicsGetCurrentContext() {
+ let scale = self.imageSize.width / self.textStickersContainer.frame.width
+ self.textStickersContainer.subviews.forEach { (sv) in
+ (sv as? ZLTextStickerView)?.resetState()
+ }
+ context.concatenate(CGAffineTransform(scaleX: scale, y: scale))
+ self.textStickersContainer.layer.render(in: context)
+ context.concatenate(CGAffineTransform(scaleX: 1/scale, y: 1/scale))
+ }
+
let temp = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgi = temp?.cgImage else {
@@ -809,7 +935,10 @@ extension ZLEditImageViewController: UIGestureRecognizerDelegate {
return true
}
} else if gestureRecognizer is UIPanGestureRecognizer {
- return (self.drawBtn.isSelected || self.mosaicBtn.isSelected) && !self.isScrolling
+ guard let st = self.selectedTool else {
+ return false
+ }
+ return (st.contains(.draw) || st.contains(.mosaic)) && !self.isScrolling
}
return true
@@ -869,7 +998,9 @@ extension ZLEditImageViewController: UIScrollViewDelegate {
extension ZLEditImageViewController: UICollectionViewDataSource, UICollectionViewDelegate {
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
- if collectionView == self.drawColorCollectionView {
+ if collectionView == self.editToolCollectionView {
+ return self.tools.count
+ } else if collectionView == self.drawColorCollectionView {
return self.drawColors.count
} else {
return self.thumbnailFilterImages.count
@@ -877,7 +1008,16 @@ extension ZLEditImageViewController: UICollectionViewDataSource, UICollectionVie
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
- if collectionView == self.drawColorCollectionView {
+ if collectionView == self.editToolCollectionView {
+ let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLEditToolCell.zl_identifier(), for: indexPath) as! ZLEditToolCell
+
+ let toolType = self.tools[indexPath.row]
+ cell.icon.isHighlighted = false
+ cell.toolType = toolType
+ cell.icon.isHighlighted = toolType == self.selectedTool
+
+ return cell
+ } else if collectionView == self.drawColorCollectionView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLDrawColorCell.zl_identifier(), for: indexPath) as! ZLDrawColorCell
let c = self.drawColors[indexPath.row]
@@ -909,7 +1049,23 @@ extension ZLEditImageViewController: UICollectionViewDataSource, UICollectionVie
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
- if collectionView == self.drawColorCollectionView {
+ if collectionView == self.editToolCollectionView {
+ let toolType = self.tools[indexPath.row]
+ switch toolType {
+ case .draw:
+ self.drawBtnClick()
+ case .clip:
+ self.clipBtnClick()
+ case .textSticker:
+ self.textStickerBtnClick()
+ case .mosaic:
+ self.mosaicBtnClick()
+ case .filter:
+ self.filterBtnClick()
+ default:
+ break
+ }
+ } else if collectionView == self.drawColorCollectionView {
self.currentDrawColor = self.drawColors[indexPath.row]
} else {
self.currentFilter = ZLPhotoConfiguration.default().filters[indexPath.row]
@@ -948,6 +1104,80 @@ extension ZLEditImageViewController: UICollectionViewDataSource, UICollectionVie
}
+extension ZLEditImageViewController: ZLTextStickerViewDelegate {
+
+ func textStickerBeginOperation(_ textSticker: ZLTextStickerView) {
+ self.setToolView(show: false)
+ self.ashbinView.layer.removeAllAnimations()
+ self.ashbinView.alpha = 0
+ UIView.animate(withDuration: 0.25) {
+ self.ashbinView.alpha = 1
+ }
+
+ self.textStickersContainer.subviews.forEach { (view) in
+ if let ts = view as? ZLTextStickerView, ts !== textSticker {
+ ts.resetState()
+ ts.gesIsEnabled = false
+ }
+ }
+ }
+
+ func textStickerOnOperation(_ textSticker: ZLTextStickerView, panGes: UIPanGestureRecognizer) {
+ let point = panGes.location(in: self.view)
+ if self.ashbinView.frame.contains(point) {
+ self.ashbinView.backgroundColor = zlRGB(241, 79, 79)
+ self.ashbinImgView.isHighlighted = true
+ } else {
+ self.ashbinView.backgroundColor = ZLEditImageViewController.ashbinNormalBgColor
+ self.ashbinImgView.isHighlighted = false
+ }
+ }
+
+ func textStickerEndOperation(_ textSticker: ZLTextStickerView, panGes: UIPanGestureRecognizer) {
+ self.setToolView(show: true)
+ self.ashbinView.layer.removeAllAnimations()
+ self.ashbinView.alpha = 0
+
+ let point = panGes.location(in: self.view)
+ if self.ashbinView.frame.contains(point) {
+ textSticker.moveToAshbin()
+ }
+
+ self.textStickersContainer.subviews.forEach { (view) in
+ (view as? ZLTextStickerView)?.gesIsEnabled = true
+ }
+ }
+
+ func textStickerDidTap(_ textSticker: ZLTextStickerView) {
+ self.textStickersContainer.subviews.forEach { (view) in
+ if let ts = view as? ZLTextStickerView, ts !== textSticker {
+ ts.resetState()
+ }
+ }
+ }
+
+ func textSticker(_ textSticker: ZLTextStickerView, editText text: String) {
+ self.showInputTextVC(text, textColor: textSticker.textColor, bgColor: textSticker.bgColor) { [weak self] (text, textColor, bgColor) in
+ guard let `self` = self else { return }
+ if text.isEmpty {
+ textSticker.moveToAshbin()
+ } else {
+ textSticker.startTimer()
+ guard textSticker.text != text else {
+ return
+ }
+ textSticker.text = text
+ textSticker.textColor = textColor
+ textSticker.bgColor = bgColor
+ let newSize = ZLTextStickerView.calculateSize(text: text, width: self.view.frame.width)
+ textSticker.changeSize(to: newSize)
+ }
+ }
+ }
+
+}
+
+
extension ZLEditImageViewController {
public struct EditImageTool: OptionSet {
@@ -962,9 +1192,11 @@ extension ZLEditImageViewController {
public static let clip = EditImageTool(rawValue: 1 << 1)
- public static let mosaic = EditImageTool(rawValue: 1 << 2)
+ public static let textSticker = EditImageTool(rawValue: 1 << 2)
+
+ public static let mosaic = EditImageTool(rawValue: 1 << 3)
- public static let filter = EditImageTool(rawValue: 1 << 3)
+ public static let filter = EditImageTool(rawValue: 1 << 4)
}
@@ -1013,6 +1245,49 @@ extension ZLImageClipRatio {
}
+// MARK: Edit tool cell
+class ZLEditToolCell: UICollectionViewCell {
+
+ var toolType: ZLEditImageViewController.EditImageTool? {
+ didSet {
+ switch toolType {
+ case .draw?:
+ self.icon.image = getImage("zl_drawLine")
+ self.icon.highlightedImage = getImage("zl_drawLine_selected")
+ case .clip?:
+ self.icon.image = getImage("zl_clip")
+ self.icon.highlightedImage = getImage("zl_clip")
+ case .textSticker?:
+ self.icon.image = getImage("zl_textSticker")
+ self.icon.highlightedImage = getImage("zl_textSticker")
+ case .mosaic?:
+ self.icon.image = getImage("zl_mosaic")
+ self.icon.highlightedImage = getImage("zl_mosaic_selected")
+ case .filter?:
+ self.icon.image = getImage("zl_filter")
+ self.icon.highlightedImage = getImage("zl_filter_selected")
+ default:
+ break
+ }
+ }
+ }
+
+ var icon: UIImageView!
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+
+ self.icon = UIImageView(frame: self.contentView.bounds)
+ self.contentView.addSubview(self.icon)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+}
+
+
// MARK: draw color cell
class ZLDrawColorCell: UICollectionViewCell {
diff --git a/Sources/Edit/ZLInputTextViewController.swift b/Sources/Edit/ZLInputTextViewController.swift
new file mode 100644
index 00000000..d4587fa7
--- /dev/null
+++ b/Sources/Edit/ZLInputTextViewController.swift
@@ -0,0 +1,206 @@
+//
+// ZLInputTextViewController.swift
+// ZLPhotoBrowser
+//
+// Created by long on 2020/10/30.
+//
+
+import UIKit
+
+class ZLInputTextViewController: UIViewController {
+
+ let image: UIImage?
+
+ var text: String
+
+ var cancelBtn: UIButton!
+
+ var doneBtn: UIButton!
+
+ var textView: UITextView!
+
+ var collectionView: UICollectionView!
+
+ var currentTextColor: UIColor
+
+ /// text, textColor, bgColor
+ var endInput: ( (String, UIColor, UIColor) -> Void )?
+
+ override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
+ return .portrait
+ }
+
+ override var prefersStatusBarHidden: Bool {
+ return true
+ }
+
+ init(image: UIImage?, text: String? = nil, textColor: UIColor? = nil, bgColor: UIColor? = nil) {
+ self.image = image
+ self.text = text ?? ""
+ if let _ = textColor {
+ self.currentTextColor = textColor!
+ } else {
+ if !ZLPhotoConfiguration.default().textStickerTextColors.contains(ZLPhotoConfiguration.default().textStickerDefaultTextColor) {
+ self.currentTextColor = ZLPhotoConfiguration.default().textStickerTextColors.first!
+ } else {
+ self.currentTextColor = ZLPhotoConfiguration.default().textStickerDefaultTextColor
+ }
+ }
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ self.setupUI()
+
+ NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIApplication.keyboardWillShowNotification, object: nil)
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+ self.textView.becomeFirstResponder()
+ }
+
+ override func viewDidLayoutSubviews() {
+ super.viewDidLayoutSubviews()
+
+ var insets = UIEdgeInsets.zero
+ if #available(iOS 11.0, *) {
+ insets = self.view.safeAreaInsets
+ }
+
+ let btnY = insets.top + 20
+ let cancelBtnW = localLanguageTextValue(.previewCancel).boundingRect(font: ZLLayout.bottomToolTitleFont, limitSize: CGSize(width: .greatestFiniteMagnitude, height: ZLLayout.bottomToolBtnH)).width + 20
+ self.cancelBtn.frame = CGRect(x: 15, y: btnY, width: cancelBtnW, height: ZLLayout.bottomToolBtnH)
+
+ let doneBtnW = localLanguageTextValue(.done).boundingRect(font: ZLLayout.bottomToolTitleFont, limitSize: CGSize(width: .greatestFiniteMagnitude, height: ZLLayout.bottomToolBtnH)).width + 20
+ self.doneBtn.frame = CGRect(x: view.bounds.width - 20 - doneBtnW, y: btnY, width: doneBtnW, height: ZLLayout.bottomToolBtnH)
+
+ self.textView.frame = CGRect(x: 20, y: cancelBtn.frame.maxY + 20, width: view.bounds.width - 40, height: 150)
+
+ if let index = ZLPhotoConfiguration.default().textStickerTextColors.firstIndex(where: { $0 == self.currentTextColor}) {
+ self.collectionView.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
+ }
+ }
+
+ func setupUI() {
+ self.view.backgroundColor = .black
+
+ let bgImageView = UIImageView(image: image?.blurImage(level: 4))
+ bgImageView.frame = self.view.bounds
+ bgImageView.contentMode = .scaleAspectFit
+ self.view.addSubview(bgImageView)
+
+ let coverView = UIView(frame: bgImageView.bounds)
+ coverView.backgroundColor = .black
+ coverView.alpha = 0.4
+ bgImageView.addSubview(coverView)
+
+ self.cancelBtn = UIButton(type: .custom)
+ self.cancelBtn.setTitle(localLanguageTextValue(.previewCancel), for: .normal)
+ self.cancelBtn.titleLabel?.font = ZLLayout.bottomToolTitleFont
+ self.cancelBtn.addTarget(self, action: #selector(cancelBtnClick), for: .touchUpInside)
+ view.addSubview(self.cancelBtn)
+
+ self.doneBtn = UIButton(type: .custom)
+ self.doneBtn.setTitle(localLanguageTextValue(.done), for: .normal)
+ self.doneBtn.titleLabel?.font = ZLLayout.bottomToolTitleFont
+ self.doneBtn.addTarget(self, action: #selector(doneBtnClick), for: .touchUpInside)
+ view.addSubview(self.doneBtn)
+
+ self.textView = UITextView(frame: .zero)
+ self.textView.keyboardAppearance = .dark
+ self.textView.returnKeyType = .done
+ self.textView.delegate = self
+ self.textView.backgroundColor = .clear
+ self.textView.tintColor = .bottomToolViewBtnNormalBgColor
+ self.textView.textColor = self.currentTextColor
+ self.textView.text = self.text
+ self.textView.font = UIFont.boldSystemFont(ofSize: ZLTextStickerView.fontSize)
+ view.addSubview(self.textView)
+
+ let layout = UICollectionViewFlowLayout()
+ layout.itemSize = CGSize(width: 30, height: 30)
+ layout.minimumLineSpacing = 15
+ layout.minimumInteritemSpacing = 15
+ layout.scrollDirection = .horizontal
+ layout.sectionInset = UIEdgeInsets(top: 10, left: 30, bottom: 10, right: 30)
+ self.collectionView = UICollectionView(frame: CGRect(x: 0, y: self.view.frame.height - 50, width: self.view.frame.width, height: 50), collectionViewLayout: layout)
+ self.collectionView.backgroundColor = .clear
+ self.collectionView.delegate = self
+ self.collectionView.dataSource = self
+ self.collectionView.showsHorizontalScrollIndicator = false
+ self.view.addSubview(self.collectionView)
+
+ ZLDrawColorCell.zl_register(self.collectionView)
+ }
+
+ @objc func cancelBtnClick() {
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func doneBtnClick() {
+ self.endInput?(self.textView.text, self.currentTextColor, .clear)
+ self.dismiss(animated: true, completion: nil)
+ }
+
+ @objc func keyboardWillShow(_ notify: Notification) {
+ let rect = notify.userInfo?[UIApplication.keyboardFrameEndUserInfoKey] as? CGRect
+ let keyboardH = rect?.height ?? 366
+ let duration: TimeInterval = notify.userInfo?[UIApplication.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0.25
+
+ UIView.animate(withDuration: duration) {
+ self.collectionView.frame = CGRect(x: 0, y: self.view.frame.height - keyboardH - 50, width: self.view.frame.width, height: 50)
+ }
+ }
+
+}
+
+
+extension ZLInputTextViewController: UICollectionViewDelegate, UICollectionViewDataSource {
+
+ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ return ZLPhotoConfiguration.default().textStickerTextColors.count
+ }
+
+ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+ let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ZLDrawColorCell.zl_identifier(), for: indexPath) as! ZLDrawColorCell
+
+ let c = ZLPhotoConfiguration.default().textStickerTextColors[indexPath.row]
+ cell.color = c
+ if c == self.currentTextColor {
+ cell.bgWhiteView.layer.transform = CATransform3DMakeScale(1.2, 1.2, 1)
+ } else {
+ cell.bgWhiteView.layer.transform = CATransform3DIdentity
+ }
+
+ return cell
+ }
+
+ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+ self.currentTextColor = ZLPhotoConfiguration.default().textStickerTextColors[indexPath.row]
+ self.textView.textColor = self.currentTextColor
+ collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
+ collectionView.reloadData()
+ }
+
+
+}
+
+
+extension ZLInputTextViewController: UITextViewDelegate {
+
+ func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
+ if text == "\n" {
+ self.doneBtnClick()
+ return false
+ }
+ return true
+ }
+
+}
diff --git a/Sources/Edit/ZLTextStickerView.swift b/Sources/Edit/ZLTextStickerView.swift
new file mode 100644
index 00000000..52168126
--- /dev/null
+++ b/Sources/Edit/ZLTextStickerView.swift
@@ -0,0 +1,449 @@
+//
+// ZLTextStickerView.swift
+// ZLPhotoBrowser
+//
+// Created by long on 2020/10/30.
+//
+
+import UIKit
+
+protocol ZLTextStickerViewDelegate: NSObject {
+
+ // Called when scale or rotate or move.
+ func textStickerBeginOperation(_ textSticker: ZLTextStickerView)
+
+ // Called during scale or rotate or move.
+ func textStickerOnOperation(_ textSticker: ZLTextStickerView, panGes: UIPanGestureRecognizer)
+
+ // Called after scale or rotate or move.
+ func textStickerEndOperation(_ textSticker: ZLTextStickerView, panGes: UIPanGestureRecognizer)
+
+ // Called when tap text sticker.
+ func textStickerDidTap(_ textSticker: ZLTextStickerView)
+
+ func textSticker(_ textSticker: ZLTextStickerView, editText text: String)
+
+}
+
+class ZLTextStickerView: UIView {
+
+ static let edgeInset: CGFloat = 20
+
+ static let fontSize: CGFloat = 30
+
+ static let borderWidth = 1 / UIScreen.main.scale
+
+ weak var delegate: ZLTextStickerViewDelegate?
+
+ var firstLayout = true
+
+ let originScale: CGFloat
+
+ let originAngle: CGFloat
+
+ var originFrame: CGRect
+
+ var originTransform: CGAffineTransform = .identity
+
+ var text: String {
+ didSet {
+ self.label.text = text
+ }
+ }
+
+ var textColor: UIColor {
+ didSet {
+ label.textColor = textColor
+ }
+ }
+
+ // TODO: add text background color
+ var bgColor: UIColor {
+ didSet {
+ label.backgroundColor = bgColor
+ }
+ }
+
+ var borderView: UIView!
+
+ var label: UILabel!
+
+ var pinchGes: UIPinchGestureRecognizer!
+
+ var tapGes: UITapGestureRecognizer!
+
+ var panGes: UIPanGestureRecognizer!
+
+ var timer: Timer?
+
+ var totalTranslationPoint: CGPoint = .zero
+
+ var gesTranslationPoint: CGPoint = .zero
+
+ var gesRotation: CGFloat = 0
+
+ var gesScale: CGFloat = 1
+
+ var onOperation = false
+
+ var gesIsEnabled = true
+
+ // Conver all states to model.
+ var state: ZLTextStickerState {
+ return ZLTextStickerState(text: self.text, textColor: self.textColor, bgColor: self.bgColor, zoomScale: self.originScale, originAngle: self.originAngle, originFrame: self.originFrame, gesScale: self.gesScale, gesRotation: self.gesRotation, totalTranslationPoint: self.totalTranslationPoint)
+ }
+
+ deinit {
+ self.cleanTimer()
+ }
+
+ convenience init(from state: ZLTextStickerState) {
+ self.init(text: state.text, textColor: state.textColor, bgColor: state.bgColor, zoomScale: state.zoomScale, originAngle: state.originAngle, originFrame: state.originFrame, gesScale: state.gesScale, gesRotation: state.gesRotation, totalTranslationPoint: state.totalTranslationPoint, showBorder: false)
+ }
+
+ init(text: String, textColor: UIColor, bgColor: UIColor, zoomScale: CGFloat, originAngle: CGFloat, originFrame: CGRect, gesScale: CGFloat = 1, gesRotation: CGFloat = 0, totalTranslationPoint: CGPoint = .zero, showBorder: Bool = true) {
+ self.originScale = zoomScale
+ self.text = text
+ self.textColor = textColor
+ self.bgColor = bgColor
+ self.originAngle = originAngle
+ self.originFrame = originFrame
+
+ super.init(frame: .zero)
+
+ self.gesScale = gesScale
+ self.gesRotation = gesRotation
+ self.totalTranslationPoint = totalTranslationPoint
+
+ self.borderView = UIView()
+ self.borderView.layer.borderWidth = ZLTextStickerView.borderWidth
+ self.hideBorder()
+ if showBorder {
+ self.startTimer()
+ }
+ self.addSubview(self.borderView)
+
+ self.label = UILabel()
+ self.label.text = text
+ self.label.font = UIFont.boldSystemFont(ofSize: ZLTextStickerView.fontSize)
+ self.label.textColor = textColor
+ self.label.backgroundColor = bgColor
+ self.label.numberOfLines = 0
+ self.label.lineBreakMode = .byCharWrapping
+ self.borderView.addSubview(self.label)
+
+ self.pinchGes = UIPinchGestureRecognizer(target: self, action: #selector(pinchAction(_:)))
+ self.pinchGes.delegate = self
+ self.addGestureRecognizer(self.pinchGes)
+
+ let rotationGes = UIRotationGestureRecognizer(target: self, action: #selector(rotationAction(_:)))
+ rotationGes.delegate = self
+ self.addGestureRecognizer(rotationGes)
+
+ self.tapGes = UITapGestureRecognizer(target: self, action: #selector(tapAction(_:)))
+ self.addGestureRecognizer(self.tapGes)
+
+ self.panGes = UIPanGestureRecognizer(target: self, action: #selector(panAction(_:)))
+ self.panGes.delegate = self
+ self.addGestureRecognizer(self.panGes)
+
+ self.tapGes.require(toFail: self.panGes)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func layoutSubviews() {
+ super.layoutSubviews()
+
+ guard self.firstLayout else {
+ return
+ }
+
+ // Rotate must be first when first layout.
+ self.transform = self.transform.rotated(by: self.originAngle.toPi)
+
+ if self.totalTranslationPoint != .zero {
+ if self.originAngle == 90 {
+ self.transform = self.transform.translatedBy(x: self.totalTranslationPoint.y, y: -self.totalTranslationPoint.x)
+ } else if self.originAngle == 180 {
+ self.transform = self.transform.translatedBy(x: -self.totalTranslationPoint.x, y: -self.totalTranslationPoint.y)
+ } else if self.originAngle == 270 {
+ self.transform = self.transform.translatedBy(x: -self.totalTranslationPoint.y, y: self.totalTranslationPoint.x)
+ } else {
+ self.transform = self.transform.translatedBy(x: self.totalTranslationPoint.x, y: self.totalTranslationPoint.y)
+ }
+ }
+
+ self.transform = self.transform.scaledBy(x: self.originScale, y: self.originScale)
+
+ self.originTransform = self.transform
+
+ if self.gesScale != 1 {
+ self.transform = self.transform.scaledBy(x: self.gesScale, y: self.gesScale)
+ }
+ if self.gesRotation != 0 {
+ self.transform = self.transform.rotated(by: self.gesRotation)
+ }
+
+ self.firstLayout = false
+ self.borderView.frame = self.bounds.insetBy(dx: ZLTextStickerView.edgeInset, dy: ZLTextStickerView.edgeInset)
+ self.label.frame = self.borderView.bounds.insetBy(dx: ZLTextStickerView.edgeInset, dy: ZLTextStickerView.edgeInset)
+ }
+
+ @objc func tapAction(_ ges: UITapGestureRecognizer) {
+ guard self.gesIsEnabled else { return }
+
+ if let t = self.timer, t.isValid {
+ self.delegate?.textSticker(self, editText: self.text)
+ } else {
+ self.superview?.bringSubviewToFront(self)
+ self.delegate?.textStickerDidTap(self)
+ self.startTimer()
+ }
+ }
+
+ @objc func pinchAction(_ ges: UIPinchGestureRecognizer) {
+ guard self.gesIsEnabled else { return }
+
+ self.gesScale *= ges.scale
+ ges.scale = 1
+
+ if ges.state == .began {
+ self.setOperation(true)
+ } else if ges.state == .changed {
+ self.updateTransform()
+ } else if (ges.state == .ended || ges.state == .cancelled){
+ self.setOperation(false)
+ }
+ }
+
+ @objc func rotationAction(_ ges: UIRotationGestureRecognizer) {
+ guard self.gesIsEnabled else { return }
+
+ self.gesRotation += ges.rotation
+ ges.rotation = 0
+
+ if ges.state == .began {
+ self.setOperation(true)
+ } else if ges.state == .changed {
+ self.updateTransform()
+ } else if (ges.state == .ended || ges.state == .cancelled){
+ self.setOperation(false)
+ }
+ }
+
+ @objc func panAction(_ ges: UIPanGestureRecognizer) {
+ guard self.gesIsEnabled else { return }
+
+ let point = ges.translation(in: self.superview)
+ self.gesTranslationPoint = CGPoint(x: point.x / self.originScale, y: point.y / self.originScale)
+
+ if ges.state == .began {
+ self.setOperation(true)
+ } else if ges.state == .changed {
+ self.updateTransform()
+ } else if (ges.state == .ended || ges.state == .cancelled) {
+ self.totalTranslationPoint.x += point.x
+ self.totalTranslationPoint.y += point.y
+ self.setOperation(false)
+ if self.originAngle == 90 {
+ self.originTransform = self.originTransform.translatedBy(x: self.gesTranslationPoint.y, y: -self.gesTranslationPoint.x)
+ } else if self.originAngle == 180 {
+ self.originTransform = self.originTransform.translatedBy(x: -self.gesTranslationPoint.x, y: -self.gesTranslationPoint.y)
+ } else if self.originAngle == 270 {
+ self.originTransform = self.originTransform.translatedBy(x: -self.gesTranslationPoint.y, y: self.gesTranslationPoint.x)
+ } else {
+ self.originTransform = self.originTransform.translatedBy(x: self.gesTranslationPoint.x, y: self.gesTranslationPoint.y)
+ }
+ self.gesTranslationPoint = .zero
+ }
+ }
+
+ func setOperation(_ isOn: Bool) {
+ if isOn, !self.onOperation {
+ self.onOperation = true
+ self.cleanTimer()
+ self.borderView.layer.borderColor = UIColor.white.cgColor
+ self.superview?.bringSubviewToFront(self)
+ self.delegate?.textStickerBeginOperation(self)
+ } else if !isOn, self.onOperation {
+ self.onOperation = false
+ self.startTimer()
+ self.delegate?.textStickerEndOperation(self, panGes: self.panGes)
+ }
+ }
+
+ func updateTransform() {
+ var transform = self.originTransform
+
+ if self.originAngle == 90 {
+ transform = transform.translatedBy(x: self.gesTranslationPoint.y, y: -self.gesTranslationPoint.x)
+ } else if self.originAngle == 180 {
+ transform = transform.translatedBy(x: -self.gesTranslationPoint.x, y: -self.gesTranslationPoint.y)
+ } else if self.originAngle == 270 {
+ transform = transform.translatedBy(x: -self.gesTranslationPoint.y, y: self.gesTranslationPoint.x)
+ } else {
+ transform = transform.translatedBy(x: self.gesTranslationPoint.x, y: self.gesTranslationPoint.y)
+ }
+ // Scale must after translate.
+ transform = transform.scaledBy(x: self.gesScale, y: self.gesScale)
+ // Rotate must after scale.
+ transform = transform.rotated(by: self.gesRotation)
+ self.transform = transform
+
+ self.delegate?.textStickerOnOperation(self, panGes: self.panGes)
+ }
+
+ @objc func hideBorder() {
+ self.borderView.layer.borderColor = UIColor.clear.cgColor
+ }
+
+ func startTimer() {
+ self.cleanTimer()
+ self.borderView.layer.borderColor = UIColor.white.cgColor
+ self.timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { (_) in
+ self.hideBorder()
+ self.cleanTimer()
+ })
+ RunLoop.current.add(self.timer!, forMode: .default)
+ }
+
+ func cleanTimer() {
+ self.timer?.invalidate()
+ self.timer = nil
+ }
+
+ func resetState() {
+ self.onOperation = false
+ self.cleanTimer()
+ self.hideBorder()
+ }
+
+ func moveToAshbin() {
+ self.cleanTimer()
+ self.removeFromSuperview()
+ }
+
+ func addScale(_ scale: CGFloat) {
+ // Revert zoom scale.
+ self.transform = self.transform.scaledBy(x: 1/self.originScale, y: 1/self.originScale)
+ // Revert ges scale.
+ self.transform = self.transform.scaledBy(x: 1/self.gesScale, y: 1/self.gesScale)
+ // Revert ges rotation.
+ self.transform = self.transform.rotated(by: -self.gesRotation)
+
+ var origin = self.frame.origin
+ origin.x *= scale
+ origin.y *= scale
+
+ let newSize = CGSize(width: self.frame.width * scale, height: self.frame.height * scale)
+ let newOrigin = CGPoint(x: self.frame.minX + (self.frame.width - newSize.width)/2, y: self.frame.minY + (self.frame.height - newSize.height)/2)
+ let diffX: CGFloat = (origin.x - newOrigin.x)
+ let diffY: CGFloat = (origin.y - newOrigin.y)
+
+ if self.originAngle == 90 {
+ self.transform = self.transform.translatedBy(x: diffY, y: -diffX)
+ self.originTransform = self.originTransform.translatedBy(x: diffY / self.originScale, y: -diffX / self.originScale)
+ } else if self.originAngle == 180 {
+ self.transform = self.transform.translatedBy(x: -diffX, y: -diffY)
+ self.originTransform = self.originTransform.translatedBy(x: -diffX / self.originScale, y: -diffY / self.originScale)
+ } else if self.originAngle == 270 {
+ self.transform = self.transform.translatedBy(x: -diffY, y: diffX)
+ self.originTransform = self.originTransform.translatedBy(x: -diffY / self.originScale, y: diffX / self.originScale)
+ } else {
+ self.transform = self.transform.translatedBy(x: diffX, y: diffY)
+ self.originTransform = self.originTransform.translatedBy(x: diffX / self.originScale, y: diffY / self.originScale)
+ }
+ self.totalTranslationPoint.x += diffX
+ self.totalTranslationPoint.y += diffY
+
+ self.transform = self.transform.scaledBy(x: scale, y: scale)
+
+ // Readd zoom scale.
+ self.transform = self.transform.scaledBy(x: self.originScale, y: self.originScale)
+ // Readd ges scale.
+ self.transform = self.transform.scaledBy(x: self.gesScale, y: self.gesScale)
+ // Readd ges rotation.
+ self.transform = self.transform.rotated(by: self.gesRotation)
+
+ self.gesScale *= scale
+ }
+
+ func changeSize(to newSize: CGSize) {
+ // Revert zoom scale.
+ self.transform = self.transform.scaledBy(x: 1/self.originScale, y: 1/self.originScale)
+ // Revert ges scale.
+ self.transform = self.transform.scaledBy(x: 1/self.gesScale, y: 1/self.gesScale)
+ // Revert ges rotation.
+ self.transform = self.transform.rotated(by: -self.gesRotation)
+ // Revert ges translation.
+ self.transform = self.transform.translatedBy(x: -self.totalTranslationPoint.x, y: -self.totalTranslationPoint.y)
+
+ // Recalculate current frame.
+ let center = CGPoint(x: self.frame.midX, y: self.frame.midY)
+ var frame = self.frame
+ frame.origin.x = center.x - newSize.width / 2
+ frame.origin.y = center.y - newSize.height / 2
+ frame.size = newSize
+ self.frame = frame
+ self.originFrame = frame
+
+ self.borderView.frame = self.bounds.insetBy(dx: ZLTextStickerView.edgeInset, dy: ZLTextStickerView.edgeInset)
+ self.label.frame = self.borderView.bounds.insetBy(dx: ZLTextStickerView.edgeInset, dy: ZLTextStickerView.edgeInset)
+
+ // Readd zoom scale.
+ self.transform = self.transform.scaledBy(x: self.originScale, y: self.originScale)
+ // Readd ges scale.
+ self.transform = self.transform.scaledBy(x: self.gesScale, y: self.gesScale)
+ // Readd ges rotation.
+ self.transform = self.transform.rotated(by: self.gesRotation)
+ // Readd ges translation.
+ self.transform = self.transform.translatedBy(x: self.totalTranslationPoint.x, y: self.totalTranslationPoint.y)
+ }
+
+ class func calculateSize(text: String, width: CGFloat) -> CGSize {
+ let diff = ZLTextStickerView.edgeInset * 2
+ let size = text.boundingRect(font: UIFont.boldSystemFont(ofSize: ZLTextStickerView.fontSize), limitSize: CGSize(width: width - diff, height: CGFloat.greatestFiniteMagnitude))
+ return CGSize(width: size.width + diff * 2, height: size.height + diff * 2)
+ }
+
+}
+
+
+extension ZLTextStickerView: UIGestureRecognizerDelegate {
+
+ func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+ return true
+ }
+
+}
+
+
+public class ZLTextStickerState: NSObject {
+
+ let text: String
+ let textColor: UIColor
+ let bgColor: UIColor
+ let zoomScale: CGFloat
+ let originAngle: CGFloat
+ let originFrame: CGRect
+ let gesScale: CGFloat
+ let gesRotation: CGFloat
+ let totalTranslationPoint: CGPoint
+
+ init(text: String, textColor: UIColor, bgColor: UIColor, zoomScale: CGFloat, originAngle: CGFloat, originFrame: CGRect, gesScale: CGFloat, gesRotation: CGFloat, totalTranslationPoint: CGPoint) {
+ self.text = text
+ self.textColor = textColor
+ self.bgColor = bgColor
+ self.zoomScale = zoomScale
+ self.originAngle = originAngle
+ self.originFrame = originFrame
+ self.gesScale = gesScale
+ self.gesRotation = gesRotation
+ self.totalTranslationPoint = totalTranslationPoint
+ super.init()
+ }
+
+}
diff --git a/Sources/Extensions/CGFloat+ZLPhotoBrowser.swift b/Sources/Extensions/CGFloat+ZLPhotoBrowser.swift
new file mode 100644
index 00000000..c77e9518
--- /dev/null
+++ b/Sources/Extensions/CGFloat+ZLPhotoBrowser.swift
@@ -0,0 +1,35 @@
+//
+// CGFloat+ZLPhotoBrowser.swift
+// ZLPhotoBrowser
+//
+// Created by long on 2020/11/10.
+//
+// Copyright (c) 2020 Long Zhang
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+import AVKit
+
+extension CGFloat {
+
+ var toPi: CGFloat {
+ return self / 180 * .pi
+ }
+
+}
diff --git a/Sources/Extensions/String+ZLPhotoBrowser.swift b/Sources/Extensions/String+ZLPhotoBrowser.swift
index 5a00e1d0..a787d738 100644
--- a/Sources/Extensions/String+ZLPhotoBrowser.swift
+++ b/Sources/Extensions/String+ZLPhotoBrowser.swift
@@ -30,8 +30,11 @@ import UIKit
extension String {
func boundingRect(font: UIFont, limitSize: CGSize) -> CGSize {
- let att = [NSAttributedString.Key.font: font]
-
+ let style = NSMutableParagraphStyle()
+ style.lineBreakMode = .byCharWrapping
+
+ let att = [NSAttributedString.Key.font: font, NSAttributedString.Key.paragraphStyle: style]
+
let attContent = NSMutableAttributedString(string: self, attributes: att)
let size = attContent.boundingRect(with: limitSize, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).size
diff --git a/Sources/Extensions/UIImage+ZLPhotoBrowser.swift b/Sources/Extensions/UIImage+ZLPhotoBrowser.swift
index bd2b4238..89175136 100644
--- a/Sources/Extensions/UIImage+ZLPhotoBrowser.swift
+++ b/Sources/Extensions/UIImage+ZLPhotoBrowser.swift
@@ -397,6 +397,24 @@ extension UIImage {
return clipImage
}
+ func blurImage(level: CGFloat) -> UIImage? {
+ guard let ciImage = self.toCIImage() else {
+ return nil
+ }
+ let blurFilter = CIFilter(name: "CIGaussianBlur")
+ blurFilter?.setValue(ciImage, forKey: "inputImage")
+ blurFilter?.setValue(level, forKey: "inputRadius")
+
+ guard let outputImage = blurFilter?.outputImage else {
+ return nil
+ }
+ let context = CIContext()
+ guard let cgImage = context.createCGImage(outputImage, from: ciImage.extent) else {
+ return nil
+ }
+ return UIImage(cgImage: cgImage)
+ }
+
}
diff --git a/Sources/General/ZLLanguageDefine.swift b/Sources/General/ZLLanguageDefine.swift
index 4feb2ca9..11162447 100644
--- a/Sources/General/ZLLanguageDefine.swift
+++ b/Sources/General/ZLLanguageDefine.swift
@@ -201,6 +201,9 @@ public struct ZLLocalLanguageKey: Hashable {
/// Unable to access all photos, go to settings (无法访问所有照片,前往设置)
public static let unableToAccessAllPhotos = ZLLocalLanguageKey(rawValue: "unableToAccessAllPhotos")
+ /// Drag here to remove (拖到此处删除)
+ public static let textStickerRemoveTips = ZLLocalLanguageKey(rawValue: "textStickerRemoveTips")
+
}
func localLanguageTextValue(_ key: ZLLocalLanguageKey) -> String {
diff --git a/Sources/General/ZLPhotoConfiguration.swift b/Sources/General/ZLPhotoConfiguration.swift
index c5277b01..df3237dc 100644
--- a/Sources/General/ZLPhotoConfiguration.swift
+++ b/Sources/General/ZLPhotoConfiguration.swift
@@ -134,7 +134,7 @@ public class ZLPhotoConfiguration: NSObject {
/// Allow to choose the minimum duration of the video.
@objc public var minSelectVideoDuration: Second = 0
- private var pri_editImageTools: ZLEditImageViewController.EditImageTool = [.draw, .clip, .mosaic, .filter]
+ private var pri_editImageTools: ZLEditImageViewController.EditImageTool = [.draw, .clip, .textSticker, .mosaic, .filter]
/// Edit image tools. (Because swift OptionSet does not support @objc mark, this attribute oc is not available)
public var editImageTools: ZLEditImageViewController.EditImageTool {
set {
@@ -142,7 +142,7 @@ public class ZLPhotoConfiguration: NSObject {
}
get {
if pri_editImageTools.isEmpty {
- return [.draw, .clip, .mosaic, .filter]
+ return [.draw, .clip, .textSticker, .mosaic, .filter]
} else {
return pri_editImageTools
}
@@ -182,6 +182,24 @@ public class ZLPhotoConfiguration: NSObject {
}
}
+ private var pri_textStickerTextColors: [UIColor] = [.white, .black, zlRGB(241, 79, 79), zlRGB(243, 170, 78), zlRGB(80, 169, 56), zlRGB(30, 183, 243), zlRGB(139, 105, 234)]
+ /// Text sticker colors for image editor.
+ @objc public var textStickerTextColors: [UIColor] {
+ set {
+ pri_textStickerTextColors = newValue
+ }
+ get {
+ if pri_textStickerTextColors.isEmpty {
+ return [.white, .black, zlRGB(241, 79, 79), zlRGB(243, 170, 78), zlRGB(80, 169, 56), zlRGB(30, 183, 243), zlRGB(139, 105, 234)]
+ } else {
+ return pri_textStickerTextColors
+ }
+ }
+ }
+
+ /// The default text sticker color. If this color not in textStickerTextColors, will pick the first color in textStickerTextColors as the default.
+ @objc public var textStickerDefaultTextColor = zlRGB(241, 79, 79)
+
private var pri_filters: [ZLFilter] = ZLFilter.all
/// Filters for image editor.
@objc public var filters: [ZLFilter] {
diff --git a/Sources/General/ZLProgressHUD.swift b/Sources/General/ZLProgressHUD.swift
index dd6889b8..24732316 100644
--- a/Sources/General/ZLProgressHUD.swift
+++ b/Sources/General/ZLProgressHUD.swift
@@ -138,6 +138,7 @@ public class ZLProgressHUD: UIView {
if timeout > 0 {
self.cleanTimer()
self.timer = Timer.scheduledTimer(timeInterval: timeout, target: self, selector: #selector(timeout(_:)), userInfo: nil, repeats: false)
+ RunLoop.current.add(self.timer!, forMode: .default)
}
}
diff --git a/Sources/General/ZLThumbnailPhotoCell.swift b/Sources/General/ZLThumbnailPhotoCell.swift
index ef96e321..8b391b6b 100644
--- a/Sources/General/ZLThumbnailPhotoCell.swift
+++ b/Sources/General/ZLThumbnailPhotoCell.swift
@@ -223,7 +223,16 @@ class ZLThumbnailPhotoCell: UICollectionViewCell {
}
func fetchSmallImage() {
- let size = CGSize(width: self.bounds.width * 1.5, height: self.bounds.height * 1.5)
+ let size: CGSize
+ if self.model.whRatio > 1 {
+ let h = self.bounds.width * 1.2
+ let w = h * self.model.whRatio
+ size = CGSize(width: w, height: h)
+ } else {
+ let w = self.bounds.width * 1.2
+ let h = w / self.model.whRatio
+ size = CGSize(width: w, height: h)
+ }
if self.smallImageRequestID > PHInvalidImageRequestID {
PHImageManager.default().cancelImageRequest(self.smallImageRequestID)
diff --git a/Sources/ZLPhotoBrowser.bundle/de.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/de.lproj/Localizable.strings
index ea1e3ce6..bc501a2c 100644
--- a/Sources/ZLPhotoBrowser.bundle/de.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/de.lproj/Localizable.strings
@@ -9,7 +9,6 @@
"editFinish" = "Fertig";
"back" = "Zurück";
-
"edit" = "Bearbeiten";
"revert" = "Rückgängig";
@@ -62,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "Alle Fotos";
"unableToAccessAllPhotos" = "Sie können nicht auf alle Fotos zugreifen und gehen Sie zu den Einstellungen";
+"textStickerRemoveTips" = "Zum Entfernen hierher ziehen";
diff --git a/Sources/ZLPhotoBrowser.bundle/en.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/en.lproj/Localizable.strings
index dd960441..fa3bacbe 100644
--- a/Sources/ZLPhotoBrowser.bundle/en.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/en.lproj/Localizable.strings
@@ -9,7 +9,6 @@
"editFinish" = "Done";
"back" = "Back";
-
"edit" = "Edit";
"revert" = "Undo";
@@ -62,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "All Photos";
"unableToAccessAllPhotos" = "Unable to access all photos, go to settings";
+"textStickerRemoveTips" = "Drag here to remove";
diff --git a/Sources/ZLPhotoBrowser.bundle/fr.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/fr.lproj/Localizable.strings
index a0ced6db..02c71636 100644
--- a/Sources/ZLPhotoBrowser.bundle/fr.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/fr.lproj/Localizable.strings
@@ -61,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "Toutes les photos";
"unableToAccessAllPhotos" = "Impossible d'accéder à toutes les photos, accédez aux paramètres";
+"textStickerRemoveTips" = "Faites glisser ici pour supprimer";
diff --git a/Sources/ZLPhotoBrowser.bundle/it.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/it.lproj/Localizable.strings
index c779d840..bbf7da70 100644
--- a/Sources/ZLPhotoBrowser.bundle/it.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/it.lproj/Localizable.strings
@@ -9,7 +9,6 @@
"editFinish" = "Fine";
"back" = "Indietro";
-
"edit" = "Modifica";
"revert" = "Annulla";
@@ -62,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "Tutte le foto";
"unableToAccessAllPhotos" = "Impossibile accedere a tutte le foto, vai alle impostazioni";
+"textStickerRemoveTips" = "Trascina qui per rimuovere";
diff --git a/Sources/ZLPhotoBrowser.bundle/ja-US.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/ja-US.lproj/Localizable.strings
index 32ec1239..2982f30b 100644
--- a/Sources/ZLPhotoBrowser.bundle/ja-US.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/ja-US.lproj/Localizable.strings
@@ -61,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "画像すべて";
"unableToAccessAllPhotos" = "すべての写真にアクセスできません。設定に移動してください";
+"textStickerRemoveTips" = "ここにドラッグして削除します";
diff --git a/Sources/ZLPhotoBrowser.bundle/ko.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/ko.lproj/Localizable.strings
index 1bc7ca1c..e27fbbd3 100644
--- a/Sources/ZLPhotoBrowser.bundle/ko.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/ko.lproj/Localizable.strings
@@ -9,7 +9,6 @@
"editFinish" = "완료";
"back" = "뒤";
-
"edit" = "편집";
"revert" = "실행 취소";
@@ -62,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "모든 사진";
"unableToAccessAllPhotos" = "모든 사진에 액세스 할 수 없습니다. 설정으로 이동하세요";
+"textStickerRemoveTips" = "제거하려면 여기로 드래그하세요";
diff --git a/Sources/ZLPhotoBrowser.bundle/ms.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/ms.lproj/Localizable.strings
index 9a23c8da..47a50d50 100644
--- a/Sources/ZLPhotoBrowser.bundle/ms.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/ms.lproj/Localizable.strings
@@ -9,7 +9,6 @@
"editFinish" = "Selesai";
"back" = "Belakang";
-
"edit" = "Edit";
"revert" = "Buat asal";
@@ -62,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "Semua Foto";
"unableToAccessAllPhotos" = "Tidak dapat mengakses semua foto, pergi ke tetapan";
+"textStickerRemoveTips" = "Seret ke sini untuk mengalih keluar";
diff --git a/Sources/ZLPhotoBrowser.bundle/ru.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/ru.lproj/Localizable.strings
index 2fc4b16f..01e63f62 100644
--- a/Sources/ZLPhotoBrowser.bundle/ru.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/ru.lproj/Localizable.strings
@@ -9,7 +9,6 @@
"editFinish" = "Готово";
"back" = "Назад";
-
"edit" = "Pед";
"revert" = "Отменить";
@@ -62,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "Все фотографии";
"unableToAccessAllPhotos" = "Невозможно получить доступ ко всем фотографиям, перейдите в настройки";
+"textStickerRemoveTips" = "Перетащите сюда, чтобы удалить";
diff --git a/Sources/ZLPhotoBrowser.bundle/vi.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/vi.lproj/Localizable.strings
index 41d6004b..f3c134bd 100644
--- a/Sources/ZLPhotoBrowser.bundle/vi.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/vi.lproj/Localizable.strings
@@ -9,7 +9,6 @@
"editFinish" = "Xong";
"back" = "Trở lại";
-
"edit" = "Chỉnh sửa";
"revert" = "Hoàn tác";
@@ -62,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "Tất cả ảnh";
"unableToAccessAllPhotos" = "Không thể truy cập tất cả ảnh, hãy chuyển đến cài đặt";
+"textStickerRemoveTips" = "Kéo vào đây để xóa";
diff --git a/Sources/ZLPhotoBrowser.bundle/zh-Hans.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/zh-Hans.lproj/Localizable.strings
index 1f3eca5d..5ef63d53 100644
--- a/Sources/ZLPhotoBrowser.bundle/zh-Hans.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/zh-Hans.lproj/Localizable.strings
@@ -61,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "所有照片";
"unableToAccessAllPhotos" = "无法访问所有照片,前往设置";
+"textStickerRemoveTips" = "拖到此处删除";
diff --git a/Sources/ZLPhotoBrowser.bundle/zh-Hant.lproj/Localizable.strings b/Sources/ZLPhotoBrowser.bundle/zh-Hant.lproj/Localizable.strings
index c07b47b5..6367970a 100644
--- a/Sources/ZLPhotoBrowser.bundle/zh-Hant.lproj/Localizable.strings
+++ b/Sources/ZLPhotoBrowser.bundle/zh-Hant.lproj/Localizable.strings
@@ -61,3 +61,4 @@
"noTitleAlbumListPlaceholder" = "所有照片";
"unableToAccessAllPhotos" = "無法訪問所有照片,前往設置";
+"textStickerRemoveTips" = "拖到此處刪除";
diff --git a/Sources/ZLPhotoBrowser.bundle/zl_ashbin@2x.png b/Sources/ZLPhotoBrowser.bundle/zl_ashbin@2x.png
new file mode 100644
index 00000000..04c2f765
Binary files /dev/null and b/Sources/ZLPhotoBrowser.bundle/zl_ashbin@2x.png differ
diff --git a/Sources/ZLPhotoBrowser.bundle/zl_ashbin@3x.png b/Sources/ZLPhotoBrowser.bundle/zl_ashbin@3x.png
new file mode 100644
index 00000000..03561d1c
Binary files /dev/null and b/Sources/ZLPhotoBrowser.bundle/zl_ashbin@3x.png differ
diff --git a/Sources/ZLPhotoBrowser.bundle/zl_ashbin_open@2x.png b/Sources/ZLPhotoBrowser.bundle/zl_ashbin_open@2x.png
new file mode 100644
index 00000000..6b2282d7
Binary files /dev/null and b/Sources/ZLPhotoBrowser.bundle/zl_ashbin_open@2x.png differ
diff --git a/Sources/ZLPhotoBrowser.bundle/zl_ashbin_open@3x.png b/Sources/ZLPhotoBrowser.bundle/zl_ashbin_open@3x.png
new file mode 100644
index 00000000..4d0e76cd
Binary files /dev/null and b/Sources/ZLPhotoBrowser.bundle/zl_ashbin_open@3x.png differ
diff --git a/Sources/ZLPhotoBrowser.bundle/zl_textSticker@2x.png b/Sources/ZLPhotoBrowser.bundle/zl_textSticker@2x.png
new file mode 100644
index 00000000..1ab2ceea
Binary files /dev/null and b/Sources/ZLPhotoBrowser.bundle/zl_textSticker@2x.png differ
diff --git a/Sources/ZLPhotoBrowser.bundle/zl_textSticker@3x.png b/Sources/ZLPhotoBrowser.bundle/zl_textSticker@3x.png
new file mode 100644
index 00000000..9c8ada2c
Binary files /dev/null and b/Sources/ZLPhotoBrowser.bundle/zl_textSticker@3x.png differ
diff --git a/UPDATELOG.md b/UPDATELOG.md
index 0d85a67a..5bff529e 100755
--- a/UPDATELOG.md
+++ b/UPDATELOG.md
@@ -8,6 +8,7 @@
* Support iOS14 limited authority.
* Provides the ability to preview PHAsset, local images and videos, network images and videos together.
* Optimize some UI effects.
+* Support show image crop vc directly.
### Fix
* Fixed the bug that the selected index is displayed in the video cell after recording the video.[#546](https://github.com/longitachi/ZLPhotoBrowser/issues/546)
diff --git a/ZLPhotoBrowser.xcodeproj/project.pbxproj b/ZLPhotoBrowser.xcodeproj/project.pbxproj
index 923c6bec..f9366214 100644
--- a/ZLPhotoBrowser.xcodeproj/project.pbxproj
+++ b/ZLPhotoBrowser.xcodeproj/project.pbxproj
@@ -31,6 +31,7 @@
E48E52D52507297500619AED /* ZLClipImageDismissAnimatedTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = E48E52D42507297500619AED /* ZLClipImageDismissAnimatedTransition.swift */; };
E492ABEB24E53575005E1BD5 /* Cell+ZLPhotoBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E492ABEA24E53575005E1BD5 /* Cell+ZLPhotoBrowser.swift */; };
E492ABED24E5454E005E1BD5 /* ZLProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E492ABEC24E5454E005E1BD5 /* ZLProgressView.swift */; };
+ E4943ABB255A652A00C29B3A /* CGFloat+ZLPhotoBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4943ABA255A652A00C29B3A /* CGFloat+ZLPhotoBrowser.swift */; };
E49BD1F724E3CD9E005D7DFB /* ZLThumbnailPhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E49BD1F624E3CD9E005D7DFB /* ZLThumbnailPhotoCell.swift */; };
E49BD1F924E3D332005D7DFB /* ZLPhotoBrowser.bundle in Resources */ = {isa = PBXBuildFile; fileRef = E49BD1F824E3D332005D7DFB /* ZLPhotoBrowser.bundle */; };
E49BD1FB24E3D515005D7DFB /* Bundle+ZLPhotoBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E49BD1FA24E3D515005D7DFB /* Bundle+ZLPhotoBrowser.swift */; };
@@ -48,6 +49,8 @@
E4CF57F024EA923C00BEBFC6 /* ZLLanguageDefine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4CF57EF24EA923C00BEBFC6 /* ZLLanguageDefine.swift */; };
E4D046F02500A341000BAEC2 /* ZLPhotoPreviewAnimatedTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D046EF2500A341000BAEC2 /* ZLPhotoPreviewAnimatedTransition.swift */; };
E4D046F22500A361000BAEC2 /* ZLPhotoPreviewPopInteractiveTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D046F12500A361000BAEC2 /* ZLPhotoPreviewPopInteractiveTransition.swift */; };
+ E4D06913254C06EB002278C4 /* ZLInputTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D06912254C06EB002278C4 /* ZLInputTextViewController.swift */; };
+ E4D06916254C0F7F002278C4 /* ZLTextStickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D06915254C0F7F002278C4 /* ZLTextStickerView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -76,6 +79,7 @@
E48E52D42507297500619AED /* ZLClipImageDismissAnimatedTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZLClipImageDismissAnimatedTransition.swift; sourceTree = ""; };
E492ABEA24E53575005E1BD5 /* Cell+ZLPhotoBrowser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Cell+ZLPhotoBrowser.swift"; sourceTree = ""; };
E492ABEC24E5454E005E1BD5 /* ZLProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZLProgressView.swift; sourceTree = ""; };
+ E4943ABA255A652A00C29B3A /* CGFloat+ZLPhotoBrowser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+ZLPhotoBrowser.swift"; sourceTree = ""; };
E49BD1F624E3CD9E005D7DFB /* ZLThumbnailPhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZLThumbnailPhotoCell.swift; sourceTree = ""; };
E49BD1F824E3D332005D7DFB /* ZLPhotoBrowser.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = ZLPhotoBrowser.bundle; sourceTree = ""; };
E49BD1FA24E3D515005D7DFB /* Bundle+ZLPhotoBrowser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+ZLPhotoBrowser.swift"; sourceTree = ""; };
@@ -95,6 +99,8 @@
E4CF57EF24EA923C00BEBFC6 /* ZLLanguageDefine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZLLanguageDefine.swift; sourceTree = ""; };
E4D046EF2500A341000BAEC2 /* ZLPhotoPreviewAnimatedTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZLPhotoPreviewAnimatedTransition.swift; sourceTree = ""; };
E4D046F12500A361000BAEC2 /* ZLPhotoPreviewPopInteractiveTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZLPhotoPreviewPopInteractiveTransition.swift; sourceTree = ""; };
+ E4D06912254C06EB002278C4 /* ZLInputTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZLInputTextViewController.swift; sourceTree = ""; };
+ E4D06915254C0F7F002278C4 /* ZLTextStickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZLTextStickerView.swift; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -169,6 +175,7 @@
E43EFA0B24F13747007067EC /* UIImage+ZLPhotoBrowser.swift */,
E45690B42530159800783AE6 /* Array+ZLPhotoBrowser.swift */,
2CC461C12551312C00BF96E8 /* Int+ZLPhotoBrowser.swift */,
+ E4943ABA255A652A00C29B3A /* CGFloat+ZLPhotoBrowser.swift */,
);
path = Extensions;
sourceTree = "";
@@ -217,6 +224,8 @@
E417AC0124F63E7E00EDDCD2 /* ZLEditImageViewController.swift */,
E46EA7D724F79F1C00033853 /* ZLClipImageViewController.swift */,
E466804624FB76C50011E332 /* ZLEditVideoViewController.swift */,
+ E4D06912254C06EB002278C4 /* ZLInputTextViewController.swift */,
+ E4D06915254C0F7F002278C4 /* ZLTextStickerView.swift */,
E410177825305C73004C4952 /* ZLFilter.swift */,
);
path = Edit;
@@ -308,8 +317,10 @@
E46EA7D824F79F1C00033853 /* ZLClipImageViewController.swift in Sources */,
E4C911E024E2ACE20061DA40 /* ZLPhotoManager.swift in Sources */,
E4CF57F024EA923C00BEBFC6 /* ZLLanguageDefine.swift in Sources */,
+ E4943ABB255A652A00C29B3A /* CGFloat+ZLPhotoBrowser.swift in Sources */,
E4765DFB25415F87007B2C0F /* ZLImagePreviewController.swift in Sources */,
E466804724FB76C50011E332 /* ZLEditVideoViewController.swift in Sources */,
+ E4D06916254C0F7F002278C4 /* ZLTextStickerView.swift in Sources */,
E462466624ECD93B00EF6C57 /* ZLAlbumListCell.swift in Sources */,
E4CF57EC24EA63BA00BEBFC6 /* ZLProgressHUD.swift in Sources */,
E4C911DC24E2A69B0061DA40 /* ZLPhotoModel.swift in Sources */,
@@ -330,6 +341,7 @@
E462465A24EB9ABF00EF6C57 /* ZLFetchImageOperation.swift in Sources */,
E4C911DE24E2AA950061DA40 /* ZLAlbumListModel.swift in Sources */,
E49BD1F724E3CD9E005D7DFB /* ZLThumbnailPhotoCell.swift in Sources */,
+ E4D06913254C06EB002278C4 /* ZLInputTextViewController.swift in Sources */,
E492ABED24E5454E005E1BD5 /* ZLProgressView.swift in Sources */,
E462466A24ED06C700EF6C57 /* ZLThumbnailViewController.swift in Sources */,
E4C911DA24E2A4910061DA40 /* ZLCustomCamera.swift in Sources */,