Skip to content

Commit

Permalink
feat: support custom waiting animation for async crop (#284)
Browse files Browse the repository at this point in the history
  • Loading branch information
guoyingtao committed Feb 25, 2023
1 parent dd47a13 commit 7800c07
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 9 deletions.
53 changes: 53 additions & 0 deletions Example/CustomWaitingIndicator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// CustomWaitingIndicator.swift
// MantisExample
//
// Created by Yingtao Guo on 2/24/23.
// Copyright © 2023 Echo. All rights reserved.
//

import Mantis
import UIKit

class CustomWaitingIndicator: UIView, Mantis.ActivityIndicatorProtocol {

private let circleLayer = CAShapeLayer()

override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}

required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}

private func setupViews() {
let circlePath = UIBezierPath(ovalIn: bounds)

circleLayer.path = circlePath.cgPath
circleLayer.strokeColor = UIColor.blue.cgColor
circleLayer.lineWidth = 2
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeEnd = 0.25
circleLayer.frame = bounds

layer.addSublayer(circleLayer)
}

func startAnimating() {
circleLayer.removeAllAnimations()
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.duration = 1
rotationAnimation.fromValue = 0
rotationAnimation.toValue = CGFloat.pi * 2
rotationAnimation.repeatCount = .infinity
circleLayer.add(rotationAnimation, forKey: "rotationAnimation")
}

func stopAnimating() {
circleLayer.removeAnimation(forKey: "rotationAnimation")
}
}

4 changes: 4 additions & 0 deletions Example/MantisExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
66F909BD287B601A007E2FC6 /* MyNavigationCropToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F909BC287B601A007E2FC6 /* MyNavigationCropToolbar.swift */; };
85DDBACD298E5A54004FBFE7 /* CustomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DDBACC298E5A54004FBFE7 /* CustomViewController.swift */; };
CCBF2F602525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBF2F5F2525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift */; };
F24DCFE529A8FA7F00D8E8C1 /* CustomWaitingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24DCFE429A8FA7F00D8E8C1 /* CustomWaitingIndicator.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -59,6 +60,7 @@
9326C1CDF121300C5CAAE2BE /* Pods-MantisExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MantisExample.debug.xcconfig"; path = "Target Support Files/Pods-MantisExample/Pods-MantisExample.debug.xcconfig"; sourceTree = "<group>"; };
B100014666B3974EEDE05B0C /* Pods_MantisExampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MantisExampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CCBF2F5F2525B3050081B8FE /* CustomizedCropToolbarWithoutList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizedCropToolbarWithoutList.swift; sourceTree = "<group>"; };
F24DCFE429A8FA7F00D8E8C1 /* CustomWaitingIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomWaitingIndicator.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -93,6 +95,7 @@
5FC10B08217A9EDF00582874 = {
isa = PBXGroup;
children = (
F24DCFE429A8FA7F00D8E8C1 /* CustomWaitingIndicator.swift */,
66F909BC287B601A007E2FC6 /* MyNavigationCropToolbar.swift */,
5F90A3CA23DCEBE800D9F27E /* MantisExample.entitlements */,
5F0852B4237918510031B75D /* ImagePicker.swift */,
Expand Down Expand Up @@ -313,6 +316,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F24DCFE529A8FA7F00D8E8C1 /* CustomWaitingIndicator.swift in Sources */,
5FD045EA245619D400B9D3D2 /* CustomizedCropToolbar.swift in Sources */,
5FC10B17217A9EDF00582874 /* ViewController.swift in Sources */,
5F0852B5237918510031B75D /* ImagePicker.swift in Sources */,
Expand Down
3 changes: 3 additions & 0 deletions Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class ViewController: UIViewController, CropViewControllerDelegate {

var config = Mantis.Config()
config.cropMode = .async

let indicatorFrame = CGRect(origin: .zero, size: config.cropViewConfig.cropActivityIndicatorSize)
config.cropViewConfig.cropActivityIndicator = CustomWaitingIndicator(frame: indicatorFrame)
config.cropToolbarConfig.toolbarButtonOptions = [.clockwiseRotate, .reset, .ratio, .horizontallyFlip]
let cropViewController = Mantis.cropViewController(image: image,
config: config)
Expand Down
26 changes: 17 additions & 9 deletions Sources/Mantis/CropView/CropView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,22 @@ class CropView: UIView {
var checkForForceFixedRatioFlag = false
let cropViewConfig: CropViewConfig

lazy private var activityIndicator: UIActivityIndicatorView = {
let activityIndicator = UIActivityIndicatorView(frame: .zero)
activityIndicator.color = .white
let indicatorSize: CGFloat = 100
activityIndicator.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)

lazy private var activityIndicator: ActivityIndicatorProtocol = {
let activityIndicator: ActivityIndicatorProtocol
if let indicator = cropViewConfig.cropActivityIndicator {
activityIndicator = indicator
} else {
activityIndicator = UIActivityIndicatorView(frame: .zero)
(activityIndicator as! UIActivityIndicatorView).color = .white
activityIndicator.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
}

addSubview(activityIndicator)
activityIndicator.translatesAutoresizingMaskIntoConstraints = false

activityIndicator.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
activityIndicator.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
activityIndicator.widthAnchor.constraint(equalToConstant: indicatorSize).isActive = true
activityIndicator.heightAnchor.constraint(equalToConstant: indicatorSize).isActive = true
activityIndicator.widthAnchor.constraint(equalToConstant: cropViewConfig.cropActivityIndicatorSize.width).isActive = true
activityIndicator.heightAnchor.constraint(equalToConstant: cropViewConfig.cropActivityIndicatorSize.width).isActive = true

return activityIndicator
}()
Expand Down Expand Up @@ -578,6 +581,7 @@ extension CropView {
DispatchQueue.global(qos: .userInteractive).async {
let maskedCropOutput = self.addImageMask(to: cropOutput)
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
completion(maskedCropOutput)
}
Expand Down Expand Up @@ -1067,3 +1071,7 @@ extension CropView: CropViewProtocol {
image.getOutputCropImageSize(by: getCropInfo())
}
}

extension UIActivityIndicatorView: ActivityIndicatorProtocol {

}
4 changes: 4 additions & 0 deletions Sources/Mantis/CropViewConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public struct CropViewConfig {
}
}

public var cropActivityIndicator: ActivityIndicatorProtocol?

public var cropActivityIndicatorSize = CGSize(width: 100, height: 100)

var minimumCropBoxSize: CGFloat = 42

public init() {}
Expand Down
5 changes: 5 additions & 0 deletions Sources/Mantis/Protocols/CropViewProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
import Foundation
import UIKit

public protocol ActivityIndicatorProtocol: UIView {
func startAnimating()
func stopAnimating()
}

protocol CropViewProtocol: UIView {
var image: UIImage { get set }
var aspectRatioLockEnabled: Bool { get set }
Expand Down

0 comments on commit 7800c07

Please sign in to comment.