From 9582bd59c754a63182cb1ada4db43c9dbb8cbe20 Mon Sep 17 00:00:00 2001 From: noppefoxwolf Date: Thu, 30 Jun 2022 22:33:24 +0900 Subject: [PATCH] Add continue pan color pick. --- Example.swiftpm/App.swift | 5 +-- Example.swiftpm/ContentView.swift | 32 ++++++++++++++- Example.swiftpm/Package.resolved | 4 +- Package.swift | 2 +- .../GoOutPanGestureRecognizer.swift | 21 ++++++++++ .../ScopeColorPicker/ScopeColorPicker.swift | 7 +++- .../ScopeColorPickerWindow.swift | 40 +++++++++++++++---- .../SliderColorPicker/ColorSlider.swift | 8 ++-- 8 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 Sources/ColorPicker/Feature/ScopeColorPicker/Components/GoOutPanGestureRecognizer.swift diff --git a/Example.swiftpm/App.swift b/Example.swiftpm/App.swift index a881c64..c2c5057 100644 --- a/Example.swiftpm/App.swift +++ b/Example.swiftpm/App.swift @@ -5,10 +5,7 @@ struct App: SwiftUI.App { var body: some Scene { WindowGroup { - HStack { - ContentView().ignoresSafeArea() - LinearGradient(colors: [.white, .black], startPoint: .top, endPoint: .bottom) - } + ContentView() } } } diff --git a/Example.swiftpm/ContentView.swift b/Example.swiftpm/ContentView.swift index 38818d2..7fbc072 100644 --- a/Example.swiftpm/ContentView.swift +++ b/Example.swiftpm/ContentView.swift @@ -1,6 +1,7 @@ import SwiftUI import ColorPicker import SnapKit +import Combine struct ContentView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> some UIViewController { @@ -12,6 +13,8 @@ struct ContentView: UIViewControllerRepresentable { } class ContentViewController: UIViewController { + var cancellables: Set = [] + override func viewDidLoad() { super.viewDidLoad() @@ -28,10 +31,37 @@ class ContentViewController: UIViewController { })) let imageView = UIImageView(image: UIImage(named: "image")) imageView.contentMode = .scaleAspectFit + let eyeDropperButton = UIButton(configuration: .filled()) + eyeDropperButton.configuration?.image = UIImage(systemName: "eyedropper") + eyeDropperButton.configuration?.title = "Tap or drag here" + eyeDropperButton.addAction(UIAction { [unowned self] _ in + let picker = ScopeColorPicker(windowScene: self.view.window!.windowScene!) + Task { + let color = await picker.pickColor() + print(color) + } + }, for: .primaryActionTriggered) + let goOut = GoOutPanGestureRecognizer() + goOut + .publisher(for: \.state) + .filter({ $0 == .began }) + .sink { [unowned self] state in + print("GO") + let picker = ScopeColorPicker( + windowScene: self.view.window!.windowScene!, + panGestureRecognizer: goOut + ) + Task { + let color = await picker.pickColor() + print(color) + } + }.store(in: &cancellables) + eyeDropperButton.addGestureRecognizer(goOut) let stackView = UIStackView(arrangedSubviews: [ imageView, colorPickerButton, - uiColorPickerButton + uiColorPickerButton, + eyeDropperButton ]) stackView.axis = .vertical view.addSubview(stackView) diff --git a/Example.swiftpm/Package.resolved b/Example.swiftpm/Package.resolved index e7ecf86..81101ad 100644 --- a/Example.swiftpm/Package.resolved +++ b/Example.swiftpm/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/SnapKit/SnapKit", "state": { "branch": null, - "revision": "d458564516e5676af9c70b4f4b2a9178294f1bc6", - "version": "5.0.1" + "revision": "f222cbdf325885926566172f6f5f06af95473158", + "version": "5.6.0" } } ] diff --git a/Package.swift b/Package.swift index 327ef93..a5aa8b3 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/SnapKit/SnapKit", from: "5.0.1"), + .package(url: "https://github.com/SnapKit/SnapKit", from: "5.6.0"), ], targets: [ .target( diff --git a/Sources/ColorPicker/Feature/ScopeColorPicker/Components/GoOutPanGestureRecognizer.swift b/Sources/ColorPicker/Feature/ScopeColorPicker/Components/GoOutPanGestureRecognizer.swift new file mode 100644 index 0000000..d331bcd --- /dev/null +++ b/Sources/ColorPicker/Feature/ScopeColorPicker/Components/GoOutPanGestureRecognizer.swift @@ -0,0 +1,21 @@ +import UIKit + +/// Viewの外に出た時に初めてbeganになるPanジェスチャ +public class GoOutPanGestureRecognizer: UIPanGestureRecognizer { + + public override func touchesBegan(_ touches: Set, with event: UIEvent) { + + } + + public override func touchesMoved(_ touches: Set, with event: UIEvent) { + if state == .possible { + let location = touches.first!.location(in: view) + if !view!.bounds.contains(location) { + super.touchesBegan(touches, with: event) + super.touchesMoved(touches, with: event) + } + } else { + super.touchesMoved(touches, with: event) + } + } +} diff --git a/Sources/ColorPicker/Feature/ScopeColorPicker/ScopeColorPicker.swift b/Sources/ColorPicker/Feature/ScopeColorPicker/ScopeColorPicker.swift index 8bb77b5..aa3b032 100644 --- a/Sources/ColorPicker/Feature/ScopeColorPicker/ScopeColorPicker.swift +++ b/Sources/ColorPicker/Feature/ScopeColorPicker/ScopeColorPicker.swift @@ -4,8 +4,11 @@ public class ScopeColorPicker { let pickerWindow: ScopeColorPickerWindow var continuation: CheckedContinuation? = nil - public init(windowScene: UIWindowScene) { - self.pickerWindow = .init(windowScene: windowScene) + public init( + windowScene: UIWindowScene, + panGestureRecognizer: UIPanGestureRecognizer? = nil + ) { + self.pickerWindow = .init(windowScene: windowScene, panGestureRecognizer: panGestureRecognizer) pickerWindow.delegate = self pickerWindow.dataSource = self } diff --git a/Sources/ColorPicker/Feature/ScopeColorPicker/ScopeColorPickerWindow.swift b/Sources/ColorPicker/Feature/ScopeColorPicker/ScopeColorPickerWindow.swift index 59b5670..637b459 100644 --- a/Sources/ColorPicker/Feature/ScopeColorPicker/ScopeColorPickerWindow.swift +++ b/Sources/ColorPicker/Feature/ScopeColorPicker/ScopeColorPickerWindow.swift @@ -15,8 +15,14 @@ class ScopeColorPickerWindow: UIWindow { weak var dataSource: ScopeColorPickerDataSource? = nil var translationX: Double = 0 var translationY: Double = 0 + let isContinuePan: Bool + weak var panGestureRecognizer: UIPanGestureRecognizer? = nil - override init(windowScene: UIWindowScene) { + init( + windowScene: UIWindowScene, + panGestureRecognizer: UIPanGestureRecognizer? = nil + ) { + isContinuePan = panGestureRecognizer != nil super.init(windowScene: windowScene) addSubview(reticleView) @@ -26,16 +32,35 @@ class ScopeColorPickerWindow: UIWindow { make.size.equalTo(viewSize) } - let panGesture = UIPanGestureRecognizer() - panGesture.addTarget(self, action: #selector(onPan(_:))) - addGestureRecognizer(panGesture) + let panGestureRecognizer = panGestureRecognizer ?? UIPanGestureRecognizer() + panGestureRecognizer.addTarget(self, action: #selector(onPan(_:))) + if !isContinuePan { + addGestureRecognizer(panGestureRecognizer) + } + self.panGestureRecognizer = panGestureRecognizer isHidden = false DispatchQueue.main.async { [weak self] in - guard let self = self else { return } + guard let self = self else { return } self.updateScopeContent(at: self.center) } + + if isContinuePan { + /// continue pan translation + let location = panGestureRecognizer.location(in: self) + let center = self.center + let x = location.x - center.x + let y = location.y - center.y + let initialTranslation = CGPoint(x: x, y: y) + self.translationX = initialTranslation.x + self.translationY = initialTranslation.y + panGestureRecognizer.setTranslation(initialTranslation, in: panGestureRecognizer.view) + reticleView.snp.updateConstraints { make in + make.centerX.equalToSuperview().offset(translationX) + make.centerY.equalToSuperview().offset(translationY) + } + } } public required init?(coder: NSCoder) { @@ -64,6 +89,7 @@ class ScopeColorPickerWindow: UIWindow { updateScopeContent(at: reticleView.center) case .ended, .failed, .cancelled: reticleView.isHidden = true + panGestureRecognizer?.removeTarget(self, action: #selector(onPan)) delegate?.scopePickerDidFinishColorPick(reticleView.color) default: break @@ -71,8 +97,8 @@ class ScopeColorPickerWindow: UIWindow { } func updateScopeContent(at location: CGPoint) { - reticleView.render { [weak self] context in - self?.dataSource?.colors(at: location, context: context) + reticleView.render { context in + dataSource?.colors(at: location, context: context) } } } diff --git a/Sources/ColorPicker/Feature/SliderColorPicker/ColorSlider.swift b/Sources/ColorPicker/Feature/SliderColorPicker/ColorSlider.swift index 19084e7..ca4f843 100644 --- a/Sources/ColorPicker/Feature/SliderColorPicker/ColorSlider.swift +++ b/Sources/ColorPicker/Feature/SliderColorPicker/ColorSlider.swift @@ -57,12 +57,12 @@ open class ColorSlider: UIControl { } trackView.addLayoutGuide(trackableLayoutGuide) trackableLayoutGuide.snp.makeConstraints { make in - make.left.right.equalToSuperview().inset(34 / 2) - make.top.bottom.equalToSuperview() + make.horizontalEdges.equalToSuperview().inset(34 / 2) + make.verticalEdges.equalToSuperview() } trackView.addLayoutGuide(trackValueLayoutGuide) trackValueLayoutGuide.snp.makeConstraints { make in - make.top.bottom.left.equalTo(trackableLayoutGuide) + make.verticalEdges.left.equalTo(trackableLayoutGuide) make.width.equalTo(trackableLayoutGuide).multipliedBy(0) } @@ -101,7 +101,7 @@ open class ColorSlider: UIControl { super.setNeedsUpdateConstraints() trackValueLayoutGuide.snp.remakeConstraints { make in - make.top.bottom.left.equalTo(trackableLayoutGuide) + make.verticalEdges.left.equalTo(trackableLayoutGuide) make.width.equalTo(trackableLayoutGuide).multipliedBy(value) } }