mirror of
https://github.com/joncardasis/ChromaColorPicker.git
synced 2024-09-11 06:35:41 +03:00
Removed legacy code and updated Fastfile to push podspec
This commit is contained in:
parent
b41d2e0cd0
commit
fb0d1e8462
1
.swift-version
Normal file
1
.swift-version
Normal file
@ -0,0 +1 @@
|
||||
4.2
|
@ -12,6 +12,6 @@ Pod::Spec.new do |s|
|
||||
s.author = "Jonathan Cardasis"
|
||||
s.platform = :ios, "9.0"
|
||||
s.source = { :git => "https://github.com/joncardasis/ChromaColorPicker.git", :tag => "#{s.version}" }
|
||||
s.source_files = "Source/*.swift"
|
||||
s.source_files = "Source/**/*.swift"
|
||||
|
||||
end
|
||||
|
@ -78,12 +78,6 @@
|
||||
FC1BD8BD2207D7B700817AF3 /* ChromaColorPickerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChromaColorPickerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FC1BD8BF2207D7B700817AF3 /* ChromaColorPickerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaColorPickerTests.swift; sourceTree = "<group>"; };
|
||||
FC1BD8C12207D7B700817AF3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
FC1BD8C82207FCE100817AF3 /* ChromaHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChromaHandle.swift; sourceTree = "<group>"; };
|
||||
FC1BD8C92207FCE100817AF3 /* ChromaShadeSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChromaShadeSlider.swift; sourceTree = "<group>"; };
|
||||
FC1BD8CA2207FCE100817AF3 /* ChromaColorPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChromaColorPicker.swift; sourceTree = "<group>"; };
|
||||
FC1BD8CB2207FCE100817AF3 /* UIColor+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FC1BD8CC2207FCE100817AF3 /* ChromaAddButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChromaAddButton.swift; sourceTree = "<group>"; };
|
||||
FC1BD8CD2207FCE100817AF3 /* ColorModeToggleButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorModeToggleButton.swift; sourceTree = "<group>"; };
|
||||
FC4387C52262556600F739F1 /* ChromaBrightnessSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromaBrightnessSlider.swift; sourceTree = "<group>"; };
|
||||
FC4387C822625C7000F739F1 /* SliderTrackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderTrackView.swift; sourceTree = "<group>"; };
|
||||
FC4387CA22625DA800F739F1 /* SliderHandleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderHandleView.swift; sourceTree = "<group>"; };
|
||||
@ -132,7 +126,6 @@
|
||||
children = (
|
||||
FC4387C722625C5F00F739F1 /* Helpers */,
|
||||
FCCA42A82260325F00BE2FF9 /* Extensions */,
|
||||
FCCA427822601F8800BE2FF9 /* Legacy */,
|
||||
3503B8311F2689BC00750356 /* ChromaColorPicker.h */,
|
||||
3503B8321F2689BC00750356 /* Info.plist */,
|
||||
FCEA4E262235AAA200C0A1B6 /* ChromaColorPicker.swift */,
|
||||
@ -200,19 +193,6 @@
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FCCA427822601F8800BE2FF9 /* Legacy */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FC1BD8CC2207FCE100817AF3 /* ChromaAddButton.swift */,
|
||||
FC1BD8CA2207FCE100817AF3 /* ChromaColorPicker.swift */,
|
||||
FC1BD8C82207FCE100817AF3 /* ChromaHandle.swift */,
|
||||
FC1BD8C92207FCE100817AF3 /* ChromaShadeSlider.swift */,
|
||||
FC1BD8CD2207FCE100817AF3 /* ColorModeToggleButton.swift */,
|
||||
FC1BD8CB2207FCE100817AF3 /* UIColor+Utilities.swift */,
|
||||
);
|
||||
path = Legacy;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FCCA42A82260325F00BE2FF9 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
125
README-Legacy.md
125
README-Legacy.md
@ -1,125 +0,0 @@
|
||||
# ChromaColorPicker :art:
|
||||
![Supported Version](https://img.shields.io/badge/Swift-4.2-yellow.svg)
|
||||
![Platform](https://img.shields.io/badge/platform-iOS-lightgray.svg)
|
||||
![License](https://img.shields.io/badge/license-MIT-blue.svg)
|
||||
![Carthage](https://img.shields.io/badge/Carthage-✔-green.svg)
|
||||
![CocoaPods](https://img.shields.io/badge/CocoaPods-1.7.1-green.svg)
|
||||
![CocoaPods](https://img.shields.io/badge/CocoaPods-1.7.1-green.svg)
|
||||
[![Travis CI](https://travis-ci.com/joncardasis/ChromaColorPicker.svg?branch=develop)](https://travis-ci.com/joncardasis/ChromaColorPicker)
|
||||
|
||||
*An intuitive iOS color picker built in Swift.*
|
||||
|
||||
Supports hue and grayscale modes to make choosing the right color easy and fun!
|
||||
|
||||
<div>
|
||||
<img src="../assets/Screenshot-With-BG.png?raw=true" height="350" style="margin:8px;">
|
||||
<img src="../assets/Screenshot-Grayscale.png?raw=true" height="350" style="margin:8px;">
|
||||
</div>
|
||||
|
||||
## Installation
|
||||
### Carthage
|
||||
```
|
||||
github "joncardasis/ChromaColorPicker"
|
||||
```
|
||||
|
||||
### Cocoapods
|
||||
```
|
||||
pod 'ChromaColorPicker'
|
||||
```
|
||||
### Manually
|
||||
Add all files from the ChromaColorPicker folder to your project.
|
||||
|
||||
|
||||
## Examples
|
||||
```Swift
|
||||
let neatColorPicker = ChromaColorPicker(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
|
||||
neatColorPicker.delegate = self //ChromaColorPickerDelegate
|
||||
neatColorPicker.padding = 5
|
||||
neatColorPicker.stroke = 3
|
||||
neatColorPicker.hexLabel.textColor = UIColor.white
|
||||
|
||||
view.addSubview(neatColorPicker)
|
||||
```
|
||||
<img src="../assets/demo.gif?raw=true" width="225">
|
||||
|
||||
If the ChromaColorPicker or any of its properties are later updated after being added to a view, the `layout()` function should be called to update the view.
|
||||
|
||||
```Swift
|
||||
let neatColorPicker = ChromaColorPicker(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
|
||||
view.addSubview(neatColorPicker)
|
||||
|
||||
neatColorPicker.padding = 0
|
||||
neatColorPicker.hexLabel.hidden = true
|
||||
|
||||
neatColorPicker.layout()
|
||||
```
|
||||
|
||||
You can also set the color of the picker anytime by using the `adjustToColor(color:)` function.
|
||||
|
||||
```Swift
|
||||
let neatColorPicker = ChromaColorPicker(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
|
||||
...
|
||||
neatColorPicker.adjustToColor(UIColor.green)
|
||||
...
|
||||
```
|
||||
|
||||
### Enable Grayscale Support
|
||||
A toggle button can be enabled/disabled to allow for grayscale selections by using the `supportsShadesOfGray` property.
|
||||
|
||||
```Swift
|
||||
let neatColorPicker = ChromaColorPicker(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
|
||||
...
|
||||
neatColorPicker.supportsShadesOfGray = true // Normally false be default
|
||||
```
|
||||
|
||||
|
||||
## Customization
|
||||
<img src="../assets/Design_Breakdown.png?raw=true" width="350">
|
||||
|
||||
### Properties
|
||||
|
||||
| Property | Description |
|
||||
| :-------------: |:-------------|
|
||||
| delegate | ChromaColorPickerDelegate |
|
||||
| padding | The padding on each side of the view (default=10) |
|
||||
| stroke | The stroke of the rainbow track (default=1) |
|
||||
| currentColor | The currently set color by the control. It is displayed in the add button. Use `adjustToColor(color: UIColor)` to update the color.|
|
||||
| currentAngle | The angle which the handle is currently sitting at. Can be changed and the view can be re-drawn using `layout()` to show the change.
|
||||
| handleSize | Returns the size of the handle. |
|
||||
| supportsShadesOfGray | True/False if a toggle for supporting grayscale colors should be shown. |
|
||||
|
||||
### Functions
|
||||
| Function | Description |
|
||||
| :-------------: |:-------------|
|
||||
|layout() | Layout the entire picker and all its subviews.|
|
||||
|adjustToColor(color: ) | Updates the picker to a specific color.|
|
||||
|
||||
### Sub-Components
|
||||
Sub-Components can be hidden and customized to the preferred liking.
|
||||
|
||||
| Component | Description |
|
||||
| :-------------: |:-------------|
|
||||
| hexLabel | A UILabel which displays the hex value of the current color. |
|
||||
| shadeSlider | A custom slider which adjusts the shade of the current color. |
|
||||
| addButton | A custom UIButton in the center of the control. The `colorPickerDidChooseColor(colorPicker: color:)` delegate function is called when this is tapped. |
|
||||
| handleView | A ChromaHandle (custom UIView) which displays the current color and can be moved around the circle.|
|
||||
| handleLine | A line which is drawn from the addButton to the handleView. |
|
||||
| colorToggleButton | A custom UIButton which appears if `supportsShadesOfGray` is set to true and will switch the picker to a grayscale mode if pressed.|
|
||||
|
||||
### Supported UIControlEvents
|
||||
`.touchDown` -> called when the handle is first grabbed
|
||||
|
||||
`.touchUpInside` -> called when handle is let go
|
||||
|
||||
`.valueChanged` -> called whenever the color has changed hue or shade
|
||||
|
||||
`.touchDragInside` -> called when the handle has moved by a drag action
|
||||
|
||||
`.editingDidEnd` -> called when either the handle is let go or slider is let go
|
||||
|
||||
|
||||
## Additional Info
|
||||
Check out the [Wiki](https://github.com/joncardasis/ChromaColorPicker/wiki/Challenges-and-Solutions) if you're interested in reading into how the color wheel was created.
|
||||
|
||||
## License
|
||||
ChromaColorPicker is available under the MIT license. See the LICENSE file for more info.
|
@ -1,94 +0,0 @@
|
||||
//
|
||||
// ChromaAddButton.swift
|
||||
//
|
||||
// Copyright © 2016 Jonathan Cardasis. All rights reserved.
|
||||
//
|
||||
// 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 UIKit
|
||||
|
||||
open class ChromaAddButton: UIButton {
|
||||
open var color = UIColor.gray{
|
||||
didSet{
|
||||
if let layer = circleLayer{
|
||||
layer.fillColor = color.cgColor
|
||||
layer.strokeColor = color.darkerColor(0.075).cgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
override open var frame: CGRect{ //update on frame change
|
||||
didSet{
|
||||
self.layoutCircleLayer()
|
||||
self.layoutPlusIconLayer()
|
||||
}
|
||||
}
|
||||
open var circleLayer: CAShapeLayer?
|
||||
open var plusIconLayer: CAShapeLayer?
|
||||
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.createGraphics()
|
||||
}
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.createGraphics()
|
||||
}
|
||||
|
||||
open func createGraphics(){
|
||||
circleLayer = CAShapeLayer()
|
||||
self.layoutCircleLayer()
|
||||
circleLayer!.fillColor = color.cgColor
|
||||
self.layer.addSublayer(circleLayer!)
|
||||
|
||||
/* Create Plus Icon */
|
||||
let plusPath = UIBezierPath()
|
||||
plusPath.move(to: CGPoint(x: self.bounds.width/2 - self.bounds.width/8, y: self.bounds.height/2))
|
||||
plusPath.addLine(to: CGPoint(x: self.bounds.width/2 + self.bounds.width/8, y: self.bounds.height/2))
|
||||
plusPath.move(to: CGPoint(x: self.bounds.width/2, y: self.bounds.height/2 + self.bounds.height/8))
|
||||
plusPath.addLine(to: CGPoint(x: self.bounds.width/2, y: self.bounds.height/2 - self.bounds.height/8))
|
||||
|
||||
plusIconLayer = CAShapeLayer()
|
||||
self.layoutPlusIconLayer()
|
||||
plusIconLayer!.strokeColor = UIColor.white.cgColor
|
||||
self.layer.addSublayer(plusIconLayer!)
|
||||
}
|
||||
|
||||
open func layoutCircleLayer(){
|
||||
if let layer = circleLayer{
|
||||
layer.path = UIBezierPath(ovalIn: self.bounds).cgPath
|
||||
layer.lineWidth = frame.width * 0.04 //4 for size (100,100)
|
||||
}
|
||||
}
|
||||
|
||||
open func layoutPlusIconLayer(){
|
||||
if let layer = plusIconLayer{
|
||||
let plusPath = UIBezierPath()
|
||||
plusPath.move(to: CGPoint(x: self.bounds.width/2 - self.bounds.width/8, y: self.bounds.height/2))
|
||||
plusPath.addLine(to: CGPoint(x: self.bounds.width/2 + self.bounds.width/8, y: self.bounds.height/2))
|
||||
plusPath.move(to: CGPoint(x: self.bounds.width/2, y: self.bounds.height/2 + self.bounds.height/8))
|
||||
plusPath.addLine(to: CGPoint(x: self.bounds.width/2, y: self.bounds.height/2 - self.bounds.height/8))
|
||||
|
||||
layer.path = plusPath.cgPath
|
||||
layer.lineWidth = frame.width * 0.03
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,520 +0,0 @@
|
||||
//
|
||||
// ChromaColorPicker.swift
|
||||
//
|
||||
// Copyright © 2016 Jonathan Cardasis. All rights reserved.
|
||||
//
|
||||
// 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 UIKit
|
||||
|
||||
public protocol ChromaColorPickerDelegate {
|
||||
/* Called when the user taps the add button in the center */
|
||||
func colorPickerDidChooseColor(_ colorPicker: ChromaColorPicker, color: UIColor)
|
||||
}
|
||||
|
||||
open class ChromaColorPicker: UIControl {
|
||||
open var hexLabel: UILabel!
|
||||
open var shadeSlider: ChromaShadeSlider!
|
||||
open var handleView: ChromaHandle!
|
||||
open var handleLine: CAShapeLayer!
|
||||
open var addButton: ChromaAddButton!
|
||||
open var colorToggleButton: ColorModeToggleButton!
|
||||
|
||||
private var modeIsGrayscale: Bool {
|
||||
return colorToggleButton.colorState == .grayscale
|
||||
}
|
||||
private enum ColorSpace {
|
||||
case rainbow
|
||||
case grayscale
|
||||
}
|
||||
|
||||
open private(set) var currentColor = UIColor.red
|
||||
open var supportsShadesOfGray: Bool = false {
|
||||
didSet {
|
||||
if supportsShadesOfGray {
|
||||
colorToggleButton.isHidden = false
|
||||
}
|
||||
else {
|
||||
colorToggleButton.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
open var delegate: ChromaColorPickerDelegate?
|
||||
open var currentAngle: Float = 0
|
||||
open private(set) var radius: CGFloat = 0
|
||||
open var stroke: CGFloat = 1
|
||||
open var padding: CGFloat = 15
|
||||
open var handleSize: CGSize{
|
||||
get{ return CGSize(width: self.bounds.width * 0.1, height: self.bounds.height * 0.1) }
|
||||
}
|
||||
|
||||
//MARK: - Initialization
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit(){
|
||||
self.backgroundColor = UIColor.clear
|
||||
|
||||
let minDimension = min(self.bounds.size.width, self.bounds.size.height)
|
||||
radius = minDimension/2 - handleSize.width/2
|
||||
|
||||
/* Setup Handle */
|
||||
handleView = ChromaHandle(frame: CGRect(x: 0,y: 0, width: handleSize.width, height: handleSize.height))
|
||||
handleView.shadowOffset = CGSize(width: 0,height: 2)
|
||||
|
||||
/* Setup pan gesture for handle */
|
||||
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ChromaColorPicker.handleWasMoved(_:)))
|
||||
handleView.addGestureRecognizer(panRecognizer)
|
||||
|
||||
/* Setup Add Button */
|
||||
addButton = ChromaAddButton()
|
||||
self.layoutAddButton() //layout frame
|
||||
addButton.addTarget(self, action: #selector(ChromaColorPicker.addButtonPressed(_:)), for: .touchUpInside)
|
||||
|
||||
/* Setup Handle Line */
|
||||
handleLine = CAShapeLayer()
|
||||
handleLine.lineWidth = 2
|
||||
handleLine.strokeColor = UIColor.white.withAlphaComponent(0.2).cgColor
|
||||
|
||||
/* Setup Color Hex Label */
|
||||
hexLabel = UILabel()
|
||||
self.layoutHexLabel() //layout frame
|
||||
hexLabel.layer.cornerRadius = 2
|
||||
hexLabel.adjustsFontSizeToFitWidth = true
|
||||
hexLabel.textAlignment = .center
|
||||
hexLabel.textColor = UIColor(red: 51/255.0, green:51/255.0, blue: 51/255.0, alpha: 0.65)
|
||||
|
||||
/* Setup Shade Slider */
|
||||
shadeSlider = ChromaShadeSlider()
|
||||
shadeSlider.delegate = self
|
||||
self.layoutShadeSlider()
|
||||
shadeSlider.addTarget(self, action: #selector(ChromaColorPicker.sliderEditingDidEnd(_:)), for: .editingDidEnd)
|
||||
|
||||
/* Setup Color Mode Toggle Button */
|
||||
colorToggleButton = ColorModeToggleButton()
|
||||
self.layoutColorToggleButton() //layout frame
|
||||
colorToggleButton.colorState = .hue // Default as starting state is hue
|
||||
colorToggleButton.addTarget(self, action: #selector(togglePickerColorMode), for: .touchUpInside)
|
||||
colorToggleButton.isHidden = !supportsShadesOfGray // default to hiding if not supported
|
||||
|
||||
/* Add components to view */
|
||||
self.layer.addSublayer(handleLine)
|
||||
self.addSubview(shadeSlider)
|
||||
self.addSubview(hexLabel)
|
||||
self.addSubview(handleView)
|
||||
self.addSubview(addButton)
|
||||
self.addSubview(colorToggleButton)
|
||||
}
|
||||
|
||||
override open func willMove(toSuperview newSuperview: UIView?) {
|
||||
/* Get the starting color */
|
||||
currentColor = colorOnWheelFromAngle(currentAngle)
|
||||
handleView.center = positionOnWheelFromAngle(currentAngle) //update pos for angle
|
||||
self.layoutHandleLine() //layout the lines positioning
|
||||
|
||||
handleView.color = currentColor
|
||||
addButton.color = currentColor
|
||||
shadeSlider.primaryColor = currentColor
|
||||
self.updateHexLabel() //update for hex value
|
||||
}
|
||||
|
||||
open func adjustToColor(_ color: UIColor){
|
||||
/* Apply saturation and brightness from previous color to current one */
|
||||
var saturation: CGFloat = 0.0
|
||||
var brightness: CGFloat = 0.0
|
||||
var hue: CGFloat = 0.0
|
||||
var alpha: CGFloat = 0.0
|
||||
|
||||
color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
|
||||
let newColor = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha)
|
||||
|
||||
/* Set the slider value for the new color and update addButton */
|
||||
shadeSlider.primaryColor = UIColor(hue: hue, saturation: 1, brightness: 1, alpha: 1) //Set a color recognzied on the color wheel
|
||||
|
||||
/* Update the angle and currentColor */
|
||||
currentAngle = angleForColor(newColor)
|
||||
currentColor = newColor
|
||||
if brightness < 1.0 && saturation < 1.0 {
|
||||
/* Modifies the Shade Slider to handle adjusting to colors outside of the Chroma scope */
|
||||
shadeSlider.primaryColor = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1)
|
||||
shadeSlider.currentValue = 0
|
||||
} else if brightness < 1.0 { //currentValue is on the left side of the slider
|
||||
shadeSlider.currentValue = brightness-1
|
||||
}else{
|
||||
shadeSlider.currentValue = -(saturation-1)
|
||||
}
|
||||
shadeSlider.updateHandleLocation() //update the handle location now that the value is set
|
||||
addButton.color = newColor
|
||||
|
||||
/* Will layout based on new angle */
|
||||
self.layoutHandle()
|
||||
self.layoutHandleLine()
|
||||
self.updateHexLabel()
|
||||
}
|
||||
|
||||
//MARK: - Handle Touches
|
||||
override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?){
|
||||
//Overriden to prevent uicontrolevents being called from the super
|
||||
}
|
||||
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?){
|
||||
let touchPoint = touches.first!.location(in: self)
|
||||
if handleView.frame.contains(touchPoint) {
|
||||
self.sendActions(for: .touchDown)
|
||||
|
||||
/* Enlarge Animation */
|
||||
UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseIn, animations: { () -> Void in
|
||||
self.handleView.transform = CGAffineTransform(scaleX: 1.45, y: 1.45)
|
||||
}, completion: nil)
|
||||
}
|
||||
}
|
||||
override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
//Run this animation after a pan or here if touches are released
|
||||
if handleView.transform.d > 1 { //if scale is larger than 1 (already animated)
|
||||
self.executeHandleShrinkAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleWasMoved(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch(recognizer.state){
|
||||
|
||||
case UIGestureRecognizer.State.changed:
|
||||
let touchPosition = recognizer.location(in: self)
|
||||
self.moveHandleTowardPoint(touchPosition)
|
||||
self.sendActions(for: .touchDragInside)
|
||||
break
|
||||
|
||||
case UIGestureRecognizer.State.ended:
|
||||
/* Shrink Animation */
|
||||
self.executeHandleShrinkAnimation()
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func executeHandleShrinkAnimation(){
|
||||
self.sendActions(for: .touchUpInside)
|
||||
self.sendActions(for: .editingDidEnd)
|
||||
UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseOut, animations: { () -> Void in
|
||||
self.handleView.transform = CGAffineTransform(scaleX: 1, y: 1)
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
private func moveHandleTowardPoint(_ point: CGPoint){
|
||||
currentAngle = angleToCenterFromPoint(point) //Find the angle of point to the frames center
|
||||
|
||||
//Layout Handle
|
||||
self.layoutHandle()
|
||||
|
||||
//Layout Line
|
||||
self.layoutHandleLine()
|
||||
|
||||
if modeIsGrayscale {
|
||||
// If mode is grayscale do not update colors and end early
|
||||
return
|
||||
}
|
||||
|
||||
//Update color for shade slider
|
||||
shadeSlider.primaryColor = handleView.color//currentColor
|
||||
|
||||
//Update color for add button if a shade isnt selected
|
||||
if shadeSlider.currentValue == 0 {
|
||||
self.updateCurrentColor(shadeSlider.currentColor)
|
||||
}
|
||||
|
||||
//Update Text Field display value
|
||||
self.updateHexLabel()
|
||||
}
|
||||
|
||||
@objc func addButtonPressed(_ sender: ChromaAddButton){
|
||||
//Do a 'bob' animation
|
||||
UIView.animate(withDuration: 0.2,
|
||||
delay: 0,
|
||||
options: .curveEaseIn,
|
||||
animations: { () -> Void in
|
||||
sender.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
|
||||
}, completion: { (done) -> Void in
|
||||
UIView.animate(withDuration: 0.1, animations: { () -> Void in
|
||||
sender.transform = CGAffineTransform(scaleX: 1, y: 1)
|
||||
})
|
||||
})
|
||||
|
||||
delegate?.colorPickerDidChooseColor(self, color: sender.color) //Delegate call
|
||||
}
|
||||
|
||||
@objc func sliderEditingDidEnd(_ sender: ChromaShadeSlider){
|
||||
self.sendActions(for: .editingDidEnd)
|
||||
}
|
||||
|
||||
//MARK: - Drawing
|
||||
override open func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
let ctx = UIGraphicsGetCurrentContext()
|
||||
let colorSpace: ColorSpace = (modeIsGrayscale) ? .grayscale : .rainbow
|
||||
|
||||
drawCircleRing(in: ctx, outerRadius: radius - padding, innerRadius: radius - stroke - padding, resolution: 1, colorSpace: colorSpace)
|
||||
}
|
||||
|
||||
/*
|
||||
Resolution should be between 0.1 and 1
|
||||
colorSpace - either rainbow or grayscale
|
||||
*/
|
||||
private func drawCircleRing(in context: CGContext?, outerRadius: CGFloat, innerRadius: CGFloat, resolution: Float, colorSpace: ColorSpace){
|
||||
context?.saveGState()
|
||||
context?.translateBy(x: self.bounds.midX, y: self.bounds.midY) //Move context to center
|
||||
|
||||
let subdivisions:CGFloat = CGFloat(resolution * 512) //Max subdivisions of 512
|
||||
|
||||
let innerHeight = (CGFloat.pi*innerRadius)/subdivisions //height of the inner wall for each segment
|
||||
let outterHeight = (CGFloat.pi*outerRadius)/subdivisions
|
||||
|
||||
let segment = UIBezierPath()
|
||||
segment.move(to: CGPoint(x: innerRadius, y: -innerHeight/2))
|
||||
segment.addLine(to: CGPoint(x: innerRadius, y: innerHeight/2))
|
||||
segment.addLine(to: CGPoint(x: outerRadius, y: outterHeight/2))
|
||||
segment.addLine(to: CGPoint(x: outerRadius, y: -outterHeight/2))
|
||||
segment.close()
|
||||
|
||||
|
||||
//Draw each segment and rotate around the center
|
||||
for i in 0 ..< Int(ceil(subdivisions)) {
|
||||
if modeIsGrayscale {
|
||||
UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1).set() //Gray
|
||||
}
|
||||
else { // Draw rainbow
|
||||
UIColor(hue: CGFloat(i)/subdivisions, saturation: 1, brightness: 1, alpha: 1).set()
|
||||
}
|
||||
|
||||
segment.fill()
|
||||
let lineTailSpace = (CGFloat.pi*2)*outerRadius/subdivisions //The amount of space between the tails of each segment
|
||||
segment.lineWidth = lineTailSpace //allows for seemless scaling
|
||||
segment.stroke()
|
||||
|
||||
//Rotate to correct location
|
||||
let rotate = CGAffineTransform(rotationAngle: -((CGFloat.pi*2)/subdivisions)) //rotates each segment
|
||||
segment.apply(rotate)
|
||||
}
|
||||
|
||||
context?.translateBy(x: -self.bounds.midX, y: -self.bounds.midY) //Move context back to original position
|
||||
context?.restoreGState()
|
||||
}
|
||||
|
||||
|
||||
//MARK: - Layout Updates
|
||||
/* Re-layout view and all its subview and drawings */
|
||||
open func layout() {
|
||||
self.setNeedsDisplay() //mark view as dirty
|
||||
|
||||
let minDimension = min(self.bounds.size.width, self.bounds.size.height)
|
||||
radius = minDimension/2 - handleSize.width/2 //create radius for new size
|
||||
|
||||
self.layoutAddButton()
|
||||
|
||||
//Update handle's size
|
||||
handleView.frame = CGRect(origin: .zero, size: handleSize)
|
||||
self.layoutHandle()
|
||||
|
||||
//Ensure colors are updated
|
||||
self.updateCurrentColor(handleView.color)
|
||||
shadeSlider.primaryColor = handleView.color
|
||||
|
||||
self.layoutShadeSlider()
|
||||
self.layoutHandleLine()
|
||||
self.layoutHexLabel()
|
||||
self.layoutColorToggleButton()
|
||||
}
|
||||
|
||||
open func layoutAddButton(){
|
||||
let addButtonSize = CGSize(width: self.bounds.width/5, height: self.bounds.height/5)
|
||||
addButton.frame = CGRect(x: self.bounds.midX - addButtonSize.width/2, y: self.bounds.midY - addButtonSize.height/2, width: addButtonSize.width, height: addButtonSize.height)
|
||||
}
|
||||
|
||||
/*
|
||||
Update the handleView's position and color for the currentAngle
|
||||
*/
|
||||
func layoutHandle(){
|
||||
let angle = currentAngle //Preserve value in case it changes
|
||||
let newPosition = positionOnWheelFromAngle(angle) //find the correct position on the color wheel
|
||||
|
||||
//Update handle position
|
||||
handleView.center = newPosition
|
||||
|
||||
if !modeIsGrayscale {
|
||||
//Update color for the movement when color mode is hue
|
||||
handleView.color = colorOnWheelFromAngle(angle)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Updates the line view's position for the current angle
|
||||
Pre: dependant on addButtons position & current angle
|
||||
*/
|
||||
func layoutHandleLine(){
|
||||
let linePath = UIBezierPath()
|
||||
linePath.move(to: addButton.center)
|
||||
linePath.addLine(to: positionOnWheelFromAngle(currentAngle))
|
||||
handleLine.path = linePath.cgPath
|
||||
}
|
||||
|
||||
/*
|
||||
Pre: dependant on addButtons position
|
||||
*/
|
||||
func layoutHexLabel(){
|
||||
hexLabel.frame = CGRect(x: 0, y: 0, width: addButton.bounds.width*1.5, height: addButton.bounds.height/3)
|
||||
hexLabel.center = CGPoint(x: self.bounds.midX, y: (addButton.frame.origin.y + (padding + handleView.frame.height/2 + stroke/2))/1.75) //Divided by 1.75 not 2 to make it a bit lower
|
||||
hexLabel.font = UIFont(name: "Menlo-Regular", size: hexLabel.bounds.height)
|
||||
}
|
||||
|
||||
/*
|
||||
Pre: dependant on radius
|
||||
*/
|
||||
func layoutShadeSlider(){
|
||||
/* Calculate proper length for slider */
|
||||
let centerPoint = CGPoint(x: bounds.midX, y: bounds.midY)
|
||||
let insideRadius = radius - padding
|
||||
|
||||
let pointLeft = CGPoint(x: centerPoint.x + insideRadius*CGFloat(cos(7*Double.pi/6)), y: centerPoint.y - insideRadius*CGFloat(sin(7*Double.pi/6)))
|
||||
let pointRight = CGPoint(x: centerPoint.x + insideRadius*CGFloat(cos(11*Double.pi/6)), y: centerPoint.y - insideRadius*CGFloat(sin(11*Double.pi/6)))
|
||||
let deltaX = pointRight.x - pointLeft.x //distance on circle between points at 7pi/6 and 11pi/6
|
||||
|
||||
|
||||
let sliderSize = CGSize(width: deltaX * 0.75, height: 0.08 * (bounds.height - padding*2))//bounds.height
|
||||
shadeSlider.frame = CGRect(x: bounds.midX - sliderSize.width/2, y: pointLeft.y - sliderSize.height/2, width: sliderSize.width, height: sliderSize.height)
|
||||
shadeSlider.handleCenterX = shadeSlider.bounds.width/2 //set handle starting position
|
||||
shadeSlider.layoutLayerFrames() //call sliders' layout function
|
||||
}
|
||||
|
||||
/*
|
||||
Pre: dependant on addButton
|
||||
*/
|
||||
func layoutColorToggleButton() {
|
||||
let inset = bounds.height/16
|
||||
colorToggleButton.frame = CGRect(x: inset, y: inset, width: addButton.frame.width/2.5, height: addButton.frame.width/2.5)
|
||||
colorToggleButton.layoutSubviews()
|
||||
}
|
||||
|
||||
func updateHexLabel(){
|
||||
hexLabel.text = "#" + currentColor.hexCode
|
||||
}
|
||||
|
||||
func updateCurrentColor(_ color: UIColor){
|
||||
currentColor = color
|
||||
addButton.color = color
|
||||
self.sendActions(for: .valueChanged)
|
||||
}
|
||||
|
||||
@objc open func togglePickerColorMode() {
|
||||
colorToggleButton.isEnabled = false // Lock
|
||||
|
||||
// Redraw Assets (i.e. Large circle ring)
|
||||
setNeedsDisplay()
|
||||
|
||||
// Update subcomponents for color change
|
||||
if modeIsGrayscale {
|
||||
//Change color of colorToggleButton to the last handle color
|
||||
let lightColor = handleView.color
|
||||
let shadedColor = handleView.color.darkerColor(0.25)
|
||||
colorToggleButton.hueColorGradientLayer.colors = [lightColor.cgColor, shadedColor.cgColor]
|
||||
|
||||
let gray = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1)
|
||||
self.handleView.color = gray
|
||||
self.updateCurrentColor(gray)
|
||||
self.updateHexLabel()
|
||||
|
||||
//Update color for shade slider
|
||||
shadeSlider.primaryColor = gray
|
||||
}
|
||||
else {
|
||||
// Update for normal rainbow
|
||||
|
||||
// Use color stored in toggle button (set above), or else default to the angle it is at
|
||||
var hueColor: UIColor
|
||||
if let storedColor = colorToggleButton.hueColorGradientLayer.colors?[0] {
|
||||
hueColor = UIColor(cgColor: (storedColor as! CGColor))
|
||||
|
||||
currentAngle = angleForColor(hueColor)
|
||||
self.layoutHandleLine()
|
||||
self.layoutHandle()
|
||||
}
|
||||
else {
|
||||
let currentAngle = self.angleToCenterFromPoint(self.handleView.center)
|
||||
hueColor = self.colorOnWheelFromAngle(currentAngle)
|
||||
}
|
||||
|
||||
self.handleView.color = hueColor
|
||||
self.updateCurrentColor(hueColor)
|
||||
self.updateHexLabel()
|
||||
|
||||
//Update color for shade slider
|
||||
shadeSlider.primaryColor = hueColor
|
||||
}
|
||||
|
||||
colorToggleButton.isEnabled = true // Unlock
|
||||
}
|
||||
|
||||
|
||||
//MARK: - Helper Methods
|
||||
private func angleToCenterFromPoint(_ point: CGPoint) -> Float {
|
||||
let deltaX = Float(self.bounds.midX - point.x)
|
||||
let deltaY = Float(self.bounds.midY - point.y)
|
||||
let angle = atan2f(deltaX, deltaY)
|
||||
|
||||
// Convert the angle to be between 0 and 2PI
|
||||
var adjustedAngle = angle + Float.pi/2
|
||||
if (adjustedAngle < 0){ //Left side (Q2 and Q3)
|
||||
adjustedAngle += Float.pi*2
|
||||
}
|
||||
|
||||
return adjustedAngle
|
||||
}
|
||||
|
||||
/* Find the angle relative to the center of the frame and uses the angle to find what color lies there */
|
||||
private func colorOnWheelFromAngle(_ angle: Float) -> UIColor {
|
||||
return UIColor(hue: CGFloat(Double(angle)/(2*Double.pi)), saturation: 1, brightness: 1, alpha: 1)
|
||||
}
|
||||
|
||||
private func angleForColor(_ color: UIColor) -> Float {
|
||||
var hue: CGFloat = 0
|
||||
color.getHue(&hue, saturation: nil, brightness: nil, alpha: nil)
|
||||
return Float(hue * CGFloat.pi * 2)
|
||||
}
|
||||
|
||||
/* Returns a position centered on the wheel for a given angle */
|
||||
private func positionOnWheelFromAngle(_ angle: Float) -> CGPoint{
|
||||
let buffer = padding + stroke/2
|
||||
return CGPoint(x: self.bounds.midX + ((radius - buffer) * CGFloat(cos(-angle))), y: self.bounds.midY + ((radius - buffer) * CGFloat(sin(-angle))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension ChromaColorPicker: ChromaShadeSliderDelegate{
|
||||
public func shadeSliderChoseColor(_ slider: ChromaShadeSlider, color: UIColor) {
|
||||
self.updateCurrentColor(color) //update main controller for selected color
|
||||
self.updateHexLabel()
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
//
|
||||
// ChromaHandle.swift
|
||||
//
|
||||
// Copyright © 2016 Jonathan Cardasis. All rights reserved.
|
||||
//
|
||||
// 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 UIKit
|
||||
|
||||
open class ChromaHandle: UIView {
|
||||
open var color = UIColor.black {
|
||||
didSet{
|
||||
circleLayer.fillColor = color.cgColor
|
||||
}
|
||||
}
|
||||
override open var frame: CGRect{
|
||||
didSet { self.layoutCircleLayer() }
|
||||
}
|
||||
open var circleLayer = CAShapeLayer()
|
||||
|
||||
open var shadowOffset: CGSize?{
|
||||
set{
|
||||
if let offset = newValue {
|
||||
circleLayer.shadowColor = UIColor.black.cgColor
|
||||
circleLayer.shadowRadius = 3
|
||||
circleLayer.shadowOpacity = 0.3
|
||||
circleLayer.shadowOffset = offset
|
||||
}
|
||||
}
|
||||
get{
|
||||
return circleLayer.shadowOffset
|
||||
}
|
||||
}
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame:frame)
|
||||
self.backgroundColor = UIColor.clear
|
||||
|
||||
/* Add Shape Layer */
|
||||
//circleLayer.shouldRasterize = true
|
||||
self.layoutCircleLayer()
|
||||
circleLayer.strokeColor = UIColor.white.cgColor
|
||||
circleLayer.fillColor = color.cgColor
|
||||
|
||||
self.layer.addSublayer(circleLayer)
|
||||
}
|
||||
|
||||
open func layoutCircleLayer(){
|
||||
circleLayer.path = UIBezierPath(ovalIn: self.bounds).cgPath
|
||||
circleLayer.strokeColor = UIColor.white.cgColor
|
||||
circleLayer.lineWidth = frame.width/8.75 //4
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
//
|
||||
// ChromaShadeSlider.swift
|
||||
//
|
||||
// Copyright © 2016 Jonathan Cardasis. All rights reserved.
|
||||
//
|
||||
// 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 UIKit
|
||||
|
||||
open class ChromaSliderTrackLayer: CALayer{
|
||||
public let gradient = CAGradientLayer()
|
||||
|
||||
override public init() {
|
||||
super.init()
|
||||
gradient.actions = ["position" : NSNull(), "bounds" : NSNull(), "path" : NSNull()]
|
||||
self.addSublayer(gradient)
|
||||
}
|
||||
override init(layer: Any) {
|
||||
super.init(layer: layer)
|
||||
}
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
public protocol ChromaShadeSliderDelegate {
|
||||
func shadeSliderChoseColor(_ slider: ChromaShadeSlider, color: UIColor)
|
||||
}
|
||||
|
||||
open class ChromaShadeSlider: UIControl {
|
||||
open var currentValue: CGFloat = 0.0 //range of {-1,1}
|
||||
|
||||
public let trackLayer = ChromaSliderTrackLayer()
|
||||
public let handleView = ChromaHandle()
|
||||
open var handleWidth: CGFloat{ return self.bounds.height }
|
||||
open var handleCenterX: CGFloat = 0.0
|
||||
open var delegate: ChromaShadeSliderDelegate?
|
||||
|
||||
open var primaryColor = UIColor.gray{
|
||||
didSet{
|
||||
self.changeColorHue(to: currentColor)
|
||||
self.updateGradientTrack(for: primaryColor)
|
||||
}
|
||||
}
|
||||
/* The computed color of the primary color with shading based on the currentValue */
|
||||
open var currentColor: UIColor{
|
||||
get{
|
||||
if currentValue < 0 {//darken
|
||||
return primaryColor.darkerColor(-currentValue)
|
||||
}
|
||||
else{ //lighten
|
||||
return primaryColor.lighterColor(currentValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
private func commonInit(){
|
||||
self.backgroundColor = nil
|
||||
handleCenterX = self.bounds.width/2
|
||||
|
||||
trackLayer.backgroundColor = UIColor.blue.cgColor
|
||||
trackLayer.masksToBounds = true
|
||||
trackLayer.actions = ["position" : NSNull(), "bounds" : NSNull(), "path" : NSNull()] //disable implicit animations
|
||||
self.layer.addSublayer(trackLayer)
|
||||
|
||||
handleView.color = UIColor.blue
|
||||
handleView.circleLayer.borderWidth = 3
|
||||
handleView.isUserInteractionEnabled = false //disable interaction for touch events
|
||||
self.addSubview(handleView)
|
||||
|
||||
let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(doubleTapRecognized))
|
||||
doubleTapGesture.numberOfTapsRequired = 2
|
||||
self.addGestureRecognizer(doubleTapGesture)
|
||||
|
||||
self.layoutLayerFrames()
|
||||
self.changeColorHue(to: currentColor)
|
||||
self.updateGradientTrack(for: primaryColor)
|
||||
}
|
||||
|
||||
override open func didMoveToSuperview() {
|
||||
self.updateGradientTrack(for: primaryColor)
|
||||
}
|
||||
|
||||
open func layoutLayerFrames(){
|
||||
trackLayer.frame = self.bounds.insetBy(dx: handleWidth/2, dy: self.bounds.height/4) //Make half the height of the bounds
|
||||
trackLayer.cornerRadius = trackLayer.bounds.height/2
|
||||
|
||||
self.updateGradientTrack(for: primaryColor)
|
||||
self.updateHandleLocation()
|
||||
self.layoutHandleFrame()
|
||||
}
|
||||
|
||||
//Lays out handle according to the currentValue on slider
|
||||
open func layoutHandleFrame(){
|
||||
handleView.frame = CGRect(x: handleCenterX - handleWidth/2, y: self.bounds.height/2 - handleWidth/2, width: handleWidth, height: handleWidth)
|
||||
}
|
||||
|
||||
open func changeColorHue(to newColor: UIColor){
|
||||
handleView.color = newColor
|
||||
if currentValue != 0 { //Don't call delegate if the color hasnt changed
|
||||
self.delegate?.shadeSliderChoseColor(self, color: newColor)
|
||||
}
|
||||
}
|
||||
|
||||
open func updateGradientTrack(for color: UIColor){
|
||||
trackLayer.gradient.frame = trackLayer.bounds
|
||||
trackLayer.gradient.startPoint = CGPoint(x: 0, y: 0.5)
|
||||
trackLayer.gradient.endPoint = CGPoint(x: 1, y: 0.5)
|
||||
|
||||
//Gradient is for astetics - the slider is actually between black and white
|
||||
trackLayer.gradient.colors = [color.darkerColor(0.65).cgColor, color.cgColor, color.lighterColor(0.65).cgColor]
|
||||
}
|
||||
|
||||
//Updates handeles location based on currentValue
|
||||
open func updateHandleLocation(){
|
||||
handleCenterX = (currentValue+1)/2 * (bounds.width - handleView.bounds.width) + handleView.bounds.width/2
|
||||
handleView.color = currentColor
|
||||
self.layoutHandleFrame()
|
||||
}
|
||||
|
||||
override open func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||
let location = touch.location(in: self)
|
||||
|
||||
if handleView.frame.contains(location) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override open func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||
let location = touch.location(in: self)
|
||||
|
||||
//Update for center point
|
||||
handleCenterX = location.x
|
||||
handleCenterX = fittedValueInBounds(handleCenterX) //adjust value to fit in bounds if needed
|
||||
|
||||
|
||||
//Update current value
|
||||
currentValue = ((handleCenterX - handleWidth/2)/trackLayer.bounds.width - 0.5) * 2 //find current value between {-1,1} of the slider
|
||||
|
||||
//Update handle color
|
||||
self.changeColorHue(to: currentColor)
|
||||
|
||||
//Update layers frames
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
self.layoutHandleFrame()
|
||||
CATransaction.commit()
|
||||
|
||||
self.sendActions(for: .valueChanged)
|
||||
return true
|
||||
}
|
||||
|
||||
override open func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||
self.sendActions(for: .editingDidEnd)
|
||||
}
|
||||
|
||||
@objc func doubleTapRecognized(_ recognizer: UITapGestureRecognizer){
|
||||
let location = recognizer.location(in: self)
|
||||
guard handleView.frame.contains(location) else {
|
||||
return
|
||||
}
|
||||
//Tap is on handle
|
||||
|
||||
resetHandleToCenter()
|
||||
}
|
||||
|
||||
open func resetHandleToCenter(){
|
||||
|
||||
handleCenterX = self.bounds.width/2
|
||||
self.layoutHandleFrame()
|
||||
handleView.color = primaryColor
|
||||
currentValue = 0.0
|
||||
|
||||
self.sendActions(for: .valueChanged)
|
||||
self.delegate?.shadeSliderChoseColor(self, color: currentColor)
|
||||
}
|
||||
|
||||
/* Helper Methods */
|
||||
//Returns a CGFloat for the highest/lowest possble value such that it is inside the views bounds
|
||||
private func fittedValueInBounds(_ value: CGFloat) -> CGFloat {
|
||||
return min(max(value, trackLayer.frame.minX), trackLayer.frame.maxX)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,119 +0,0 @@
|
||||
//
|
||||
// ColorModeToggleButton.swift
|
||||
//
|
||||
// Copyright © 2017 Jonathan Cardasis. All rights reserved.
|
||||
//
|
||||
// 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 UIKit
|
||||
|
||||
open class ColorModeToggleButton: UIButton {
|
||||
|
||||
public enum ColorState {
|
||||
case hue
|
||||
case grayscale
|
||||
}
|
||||
|
||||
open var colorState: ColorState = .hue
|
||||
|
||||
open lazy var hueColorGradientLayer: CAGradientLayer = {
|
||||
let gradient = CAGradientLayer()
|
||||
let colorUpperLeft = UIColor(red: 250/255.0, green: 217/255.0, blue: 97/255.0, alpha: 1)
|
||||
let colorLowerRight = UIColor(red: 247/255.0, green: 107/255.0, blue: 28/255.0, alpha: 1)
|
||||
gradient.colors = [colorUpperLeft.cgColor, colorLowerRight.cgColor]
|
||||
gradient.startPoint = .zero
|
||||
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
|
||||
gradient.frame = self.bounds
|
||||
return gradient
|
||||
}()
|
||||
|
||||
open lazy var grayColorGradientLayer: CAGradientLayer = {
|
||||
let gradient = CAGradientLayer()
|
||||
let gray = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1)
|
||||
let colorUpperLeft = gray
|
||||
let colorLowerRight = gray.darkerColor(0.25)
|
||||
gradient.colors = [colorUpperLeft.cgColor, colorLowerRight.cgColor]
|
||||
gradient.startPoint = .zero
|
||||
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
|
||||
gradient.frame = self.bounds
|
||||
return gradient
|
||||
}()
|
||||
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
layer.backgroundColor = UIColor.clear.cgColor
|
||||
layer.addSublayer(hueColorGradientLayer)
|
||||
layer.addSublayer(grayColorGradientLayer)
|
||||
|
||||
addTarget(self, action: #selector(toggleState), for: .touchUpInside)
|
||||
}
|
||||
|
||||
// override open func sendActions(for controlEvents: UIControlEvents) {
|
||||
// if controlEvents == .touchUpInside {
|
||||
// toggleState()
|
||||
// }
|
||||
// super.sendActions(for: controlEvents)
|
||||
// }
|
||||
//
|
||||
override open func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
layer.masksToBounds = true
|
||||
layer.cornerRadius = bounds.width/2
|
||||
layoutGradientLayer()
|
||||
}
|
||||
|
||||
@objc open func toggleState() {
|
||||
if colorState == .hue {
|
||||
colorState = .grayscale
|
||||
}
|
||||
else {
|
||||
colorState = .hue
|
||||
}
|
||||
|
||||
layoutGradientLayer()
|
||||
}
|
||||
|
||||
private func layoutGradientLayer() {
|
||||
hueColorGradientLayer.frame = bounds
|
||||
grayColorGradientLayer.frame = bounds
|
||||
|
||||
if colorState == .hue {
|
||||
// Display inverse gradients. (i.e. Gray is displayed when hue is active)
|
||||
grayColorGradientLayer.isHidden = false
|
||||
hueColorGradientLayer.isHidden = true
|
||||
}
|
||||
else {
|
||||
hueColorGradientLayer.isHidden = false
|
||||
grayColorGradientLayer.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
//
|
||||
// UIColor+Utilities.swift
|
||||
//
|
||||
// Copyright © 2016 Jonathan Cardasis. All rights reserved.
|
||||
//
|
||||
// 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 UIKit
|
||||
public extension UIColor{
|
||||
public var hexCode: String {
|
||||
get{
|
||||
let colorComponents = self.cgColor.components!
|
||||
if colorComponents.count < 4 {
|
||||
return String(format: "%02x%02x%02x", Int(colorComponents[0]*255.0), Int(colorComponents[0]*255.0),Int(colorComponents[0]*255.0)).uppercased()
|
||||
}
|
||||
return String(format: "%02x%02x%02x", Int(colorComponents[0]*255.0), Int(colorComponents[1]*255.0),Int(colorComponents[2]*255.0)).uppercased()
|
||||
}
|
||||
}
|
||||
|
||||
//Amount should be between 0 and 1
|
||||
public func lighterColor(_ amount: CGFloat) -> UIColor{
|
||||
return UIColor.blendColors(color: self, destinationColor: UIColor.white, amount: amount)
|
||||
}
|
||||
|
||||
public func darkerColor(_ amount: CGFloat) -> UIColor{
|
||||
return UIColor.blendColors(color: self, destinationColor: UIColor.black, amount: amount)
|
||||
}
|
||||
|
||||
public static func blendColors(color: UIColor, destinationColor: UIColor, amount : CGFloat) -> UIColor{
|
||||
var amountToBlend = amount;
|
||||
if amountToBlend > 1{
|
||||
amountToBlend = 1.0
|
||||
}
|
||||
else if amountToBlend < 0{
|
||||
amountToBlend = 0
|
||||
}
|
||||
|
||||
var r,g,b, alpha : CGFloat
|
||||
r = 0
|
||||
g = 0
|
||||
b = 0
|
||||
alpha = 0
|
||||
color.getRed(&r, green: &g, blue: &b, alpha: &alpha) //gets the rgba values (0-1)
|
||||
|
||||
//Get the destination rgba values
|
||||
var dest_r, dest_g, dest_b, dest_alpha : CGFloat
|
||||
dest_r = 0
|
||||
dest_g = 0
|
||||
dest_b = 0
|
||||
dest_alpha = 0
|
||||
destinationColor.getRed(&dest_r, green: &dest_g, blue: &dest_b, alpha: &dest_alpha)
|
||||
|
||||
r = amountToBlend * (dest_r * 255) + (1 - amountToBlend) * (r * 255)
|
||||
g = amountToBlend * (dest_g * 255) + (1 - amountToBlend) * (g * 255)
|
||||
b = amountToBlend * (dest_b * 255) + (1 - amountToBlend) * (b * 255)
|
||||
alpha = abs(alpha / dest_alpha)
|
||||
|
||||
return UIColor(red: r/255.0, green: g/255.0, blue: b/255.0, alpha: alpha)
|
||||
}
|
||||
|
||||
}
|
@ -4,29 +4,28 @@ default_platform(:ios)
|
||||
|
||||
platform :ios do
|
||||
|
||||
desc "Runs unit tests, publishing a new pod version if tests succeed."
|
||||
lane :test_and_publish do
|
||||
test
|
||||
publish_pod
|
||||
end
|
||||
|
||||
desc "Publish podspec, incrementing the patch version."
|
||||
lane :publish_pod do
|
||||
publish_podspec
|
||||
end
|
||||
|
||||
desc "Runs unit tests. Installs pods if needed."
|
||||
desc "Runs unit tests."
|
||||
lane :test do
|
||||
run_tests(scheme: "ChromaColorPickerTests")
|
||||
publish_pod
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def app_unit_tests(options)
|
||||
unit_tests(options)
|
||||
generate_tests_coverage(options) if options[:code_coverage]
|
||||
end
|
||||
|
||||
|
||||
|
||||
def publish_podspec
|
||||
podspec = ENV['PODSPEC']
|
||||
version = bump_podspec_version(podspec)
|
||||
version = version_bump_podspec(path: podspec, bump_type: "patch")
|
||||
puts "HERE: #{version}"
|
||||
commit(podspec, 'Updated Podspec version.')
|
||||
push_to_git_remote(local_branch: git_branch)
|
||||
tag_build(version)
|
||||
@ -48,3 +47,15 @@ def tag_build(tag)
|
||||
)
|
||||
push_git_tags
|
||||
end
|
||||
|
||||
def commit(path, message)
|
||||
# Ensure we are on the correct branch before committing.
|
||||
sh "git checkout #{git_branch}"
|
||||
ensure_git_branch(branch: git_branch)
|
||||
|
||||
message = "Fastfile: #{message}"
|
||||
#git_commit(
|
||||
# path: path,
|
||||
# message: message
|
||||
#)
|
||||
end
|
@ -16,16 +16,21 @@ or alternatively using `brew cask install fastlane`
|
||||
|
||||
# Available Actions
|
||||
## iOS
|
||||
### ios pod_publish
|
||||
### ios test_and_publish
|
||||
```
|
||||
fastlane ios pod_publish
|
||||
fastlane ios test_and_publish
|
||||
```
|
||||
Runs unit tests, publishing a new pod version if tests succeed.
|
||||
### ios publish_pod
|
||||
```
|
||||
fastlane ios publish_pod
|
||||
```
|
||||
Publish podspec, incrementing the patch version.
|
||||
### ios test
|
||||
```
|
||||
fastlane ios test
|
||||
```
|
||||
Runs unit tests. Installs pods if needed.
|
||||
Runs unit tests.
|
||||
|
||||
----
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user