├── .gitignore ├── .swift-version ├── .travis.yml ├── Assets ├── EFColorPicker.png ├── sample_ipad.gif ├── sample_iphone.gif ├── sample_iphone.png ├── sample_iphone1.png ├── sample_iphone2.png ├── sample_iphone3.png └── sample_iphone4.png ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── EFColorPicker.podspec ├── EFColorPicker ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── EFColorComponentView.swift │ ├── EFColorSelectionView.swift │ ├── EFColorSelectionViewController.swift │ ├── EFColorUtils.swift │ ├── EFColorView.swift │ ├── EFColorWheelView.swift │ ├── EFControl.swift │ ├── EFHSBView.swift │ ├── EFRGBView.swift │ ├── EFSliderView.swift │ └── EFThumbView.swift ├── Example ├── EFColorPicker.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── EFColorPicker.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── EFColorPicker │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── 1024.png │ │ │ ├── 167.png │ │ │ ├── 20.png │ │ │ ├── 20@2x-1.png │ │ │ ├── 20@2x-2.png │ │ │ ├── 20@2x.png │ │ │ ├── 20@3x.png │ │ │ ├── 29.png │ │ │ ├── 29@2x-1.png │ │ │ ├── 29@2x.png │ │ │ ├── 29@3x.png │ │ │ ├── 40@2x-1.png │ │ │ ├── 40@2x.png │ │ │ ├── 40@3x.png │ │ │ ├── 60@2x.png │ │ │ ├── 60@3x.png │ │ │ ├── 76.png │ │ │ ├── 76@2x.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── welcome.imageset │ │ │ ├── Contents.json │ │ │ └── welcome.jpg │ ├── Info.plist │ └── ViewController.swift ├── Podfile └── Podfile.lock ├── ISSUE_TEMPLATE.md ├── LICENSE ├── Package.swift ├── README.md ├── README_CN.md ├── Startup.sh └── _config.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | .swiftpm 22 | 23 | # Bundler 24 | .bundle 25 | 26 | Carthage 27 | # We recommend against adding the Pods directory to your .gitignore. However 28 | # you should judge for yourself, the pros and cons are mentioned at: 29 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 30 | # 31 | # Note: if you ignore the Pods directory, make sure to uncomment 32 | # `pod install` in .travis.yml 33 | # 34 | Pods/ 35 | 36 | # SPM 37 | .swiftpm 38 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode11.3 3 | env: SWIFT_SNAPSHOT=5.0 4 | 5 | cache: cocoapods 6 | podfile: Example/Podfile 7 | 8 | env: 9 | global: 10 | - LANG=en_US.UTF-8 11 | - LC_ALL=en_US.UTF-8 12 | - XCODE_WORKSPACE=Example/EFColorPicker.xcworkspace 13 | - DESTINATION="OS=12.0,name=iPhone XS" 14 | matrix: 15 | - SCHEME="EFColorPicker-Example" 16 | 17 | before_install: 18 | - gem install xcpretty --no-document --quiet 19 | - gem install cocoapods --pre --no-document --quiet 20 | - pod install --project-directory=Example 21 | 22 | script: 23 | - set -o pipefail 24 | - pod repo update 25 | - xcodebuild -workspace "$XCODE_WORKSPACE" -scheme "$SCHEME" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty -c; 26 | - xcodebuild -workspace "$XCODE_WORKSPACE" -scheme "$SCHEME" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO build | xcpretty -c; 27 | - pod lib lint --allow-warnings 28 | 29 | after_success: 30 | - sleep 3 31 | -------------------------------------------------------------------------------- /Assets/EFColorPicker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Assets/EFColorPicker.png -------------------------------------------------------------------------------- /Assets/sample_ipad.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Assets/sample_ipad.gif -------------------------------------------------------------------------------- /Assets/sample_iphone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Assets/sample_iphone.gif -------------------------------------------------------------------------------- /Assets/sample_iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Assets/sample_iphone.png -------------------------------------------------------------------------------- /Assets/sample_iphone1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Assets/sample_iphone1.png -------------------------------------------------------------------------------- /Assets/sample_iphone2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Assets/sample_iphone2.png -------------------------------------------------------------------------------- /Assets/sample_iphone3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Assets/sample_iphone3.png -------------------------------------------------------------------------------- /Assets/sample_iphone4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Assets/sample_iphone4.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ----- 4 | 5 | ## [5.2.2](https://github.com/EFPrefix/EFColorPicker/releases/tag/5.2.2) (2021-07-19) 6 | 7 | #### Fix 8 | 9 | * Fix issue [#25](https://github.com/EFPrefix/EFColorPicker/issues/25). 10 | 11 | --- 12 | 13 | ## [5.2.1](https://github.com/EFPrefix/EFColorPicker/releases/tag/5.2.1) (2020-09-04) 14 | 15 | #### Fix 16 | 17 | * Fix issue [#32](https://github.com/EFPrefix/EFColorPicker/issues/32). 18 | 19 | --- 20 | 21 | ## [5.2.0](https://github.com/EFPrefix/EFColorPicker/releases/tag/5.2.0) (2020-02-15) 22 | 23 | - Add SwiftPM support; 24 | - iOS 13 Dark Mode support. 25 | 26 | --- 27 | 28 | ## [5.1.0](https://github.com/EFPrefix/EFColorPicker/releases/tag/5.1.0) (2019-08-02) 29 | 30 | - Add parameter `mode`, issue [#22](https://github.com/EFPrefix/EFColorPicker/issues/22). 31 | 32 | --- 33 | 34 | ## [5.0.0](https://github.com/EFPrefix/EFColorPicker/releases/tag/5.0.0) (2019-03-31) 35 | 36 | - Upgrade to Swift 5.0. 37 | 38 | --- 39 | 40 | ## [1.2.1](https://github.com/EFPrefix/EFColorPicker/releases/tag/1.2.1) (2019-01-13) 41 | 42 | #### Add 43 | 44 | * Make accessibilityIgnoresInvertColors. 45 | 46 | --- 47 | 48 | ## [1.2.0](https://github.com/EFPrefix/EFColorPicker/releases/tag/1.2.0) (2018-11-05) 49 | 50 | #### Add 51 | 52 | * Upgrade to Swift 4.2. 53 | 54 | --- 55 | 56 | ## [1.1.0](https://github.com/EFPrefix/EFColorPicker/releases/tag/1.1.0) (2018-03-30) 57 | 58 | #### Fix 59 | 60 | * Fix issue [#4](https://github.com/EFPrefix/EFColorPicker/issues/4). 61 | 62 | --- 63 | 64 | ## [1.0.0](https://github.com/EFPrefix/EFColorPicker/releases/tag/1.0.0) (2017-10-20) 65 | 66 | #### Add 67 | 68 | * Add ability to control the visibility of color textFiels. 69 | 70 | --- 71 | 72 | ## [0.0.1](https://github.com/EFPrefix/EFColorPicker/releases/tag/0.0.1) (2017-09-30) 73 | 74 | First public release. 75 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at eyrefree@eyrefree.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | This document contains information and guidelines about contributing to this project. 4 | Please read it before you start participating. 5 | 6 | **Topics** 7 | 8 | * [Asking Questions](#asking-questions) 9 | * [Reporting Issues](#reporting-issues) 10 | * [Developers Certificate of Origin](#developers-certificate-of-origin) 11 | 12 | ## Asking Questions 13 | 14 | We don't use GitHub as a support forum. 15 | For any usage questions that are not specific to the project itself, 16 | please ask on [Stack Overflow](https://stackoverflow.com) instead. 17 | By doing so, you'll be more likely to quickly solve your problem, 18 | and you'll allow anyone else with the same question to find the answer. 19 | This also allows maintainers to focus on improving the project for others. 20 | 21 | ## Reporting Issues 22 | 23 | A great way to contribute to the project 24 | is to send a detailed issue when you encounter an problem. 25 | We always appreciate a well-written, thorough bug report. 26 | 27 | Check that the project issues database 28 | doesn't already include that problem or suggestion before submitting an issue. 29 | If you find a match, add a quick "+1" or "I have this problem too." 30 | Doing this helps prioritize the most common problems and requests. 31 | 32 | When reporting issues, please include the following: 33 | 34 | * The version of Xcode you're using 35 | * The version of iOS or macOS you're targeting 36 | * The full output of any stack trace or compiler error 37 | * A code snippet that reproduces the described behavior, if applicable 38 | * Any other details that would be useful in understanding the problem 39 | 40 | This information will help us review and fix your issue faster. 41 | 42 | ## Developer's Certificate of Origin 43 | 44 | By making a contribution to this project, I certify that: 45 | 46 | - (a) The contribution was created in whole or in part by me and I 47 | have the right to submit it under the open source license 48 | indicated in the file; or 49 | 50 | - (b) The contribution is based upon previous work that, to the best 51 | of my knowledge, is covered under an appropriate open source 52 | license and I have the right under that license to submit that 53 | work with modifications, whether created in whole or in part 54 | by me, under the same open source license (unless I am 55 | permitted to submit under a different license), as indicated 56 | in the file; or 57 | 58 | - (c) The contribution was provided directly to me by some other 59 | person who certified (a), (b) or (c) and I have not modified 60 | it. 61 | 62 | - (d) I understand and agree that this project and the contribution 63 | are public and that a record of the contribution (including all 64 | personal information I submit with it, including my sign-off) is 65 | maintained indefinitely and may be redistributed consistent with 66 | this project or the open source license(s) involved. 67 | 68 | *Some of the ideas and wording for the statements above were based on work by the [Alamofire](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md) communities. We commend them for their efforts to facilitate collaboration in their projects.* 69 | -------------------------------------------------------------------------------- /EFColorPicker.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'EFColorPicker' 3 | s.version = '5.2.2' 4 | s.summary = 'A lightweight color picker in Swift.' 5 | 6 | s.description = <<-DESC 7 | EFColorPicker is a lightweight color picker in Swift, inspired by MSColorPicker. 8 | DESC 9 | 10 | s.homepage = 'https://github.com/EFPrefix/EFColorPicker' 11 | s.screenshots = 'https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/EFColorPicker.png' 12 | s.license = { :type => 'MIT', :file => 'LICENSE' } 13 | s.author = { 'EyreFree' => 'eyrefree@eyrefree.org' } 14 | s.source = { :git => 'https://github.com/EFPrefix/EFColorPicker.git', :tag => s.version.to_s } 15 | s.social_media_url = 'https://twitter.com/EyreFree777' 16 | 17 | s.ios.deployment_target = '8.0' 18 | s.requires_arc = true 19 | s.source_files = 'EFColorPicker/Classes/**/*' 20 | end 21 | -------------------------------------------------------------------------------- /EFColorPicker/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/EFColorPicker/Assets/.gitkeep -------------------------------------------------------------------------------- /EFColorPicker/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/EFColorPicker/Classes/.gitkeep -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFColorComponentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFColorComponentView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | // The view to edit a color component. 30 | public class EFColorComponentView: UIControl { 31 | 32 | // Indicates if the user touches the control at the moment 33 | public var isTouched: Bool { 34 | return slider.isTouched 35 | } 36 | 37 | // Temporary disabled the color component editing via text field 38 | public var colorTextFieldEnabled = false { 39 | didSet { 40 | if textField.isHidden == colorTextFieldEnabled { 41 | ef_remakeConstraints() 42 | textField.isHidden = !colorTextFieldEnabled 43 | } 44 | } 45 | } 46 | let EFColorComponentViewSpacing: CGFloat = 5.0 47 | let EFColorComponentLabelWidth: CGFloat = 60.0 48 | let EFColorComponentTextFieldWidth: CGFloat = 50.0 49 | 50 | // The title. 51 | var title: String { 52 | get { 53 | return label.text ?? "" 54 | } 55 | set { 56 | label.text = newValue 57 | } 58 | } 59 | 60 | // The current value. The default value is 0.0. 61 | var value: CGFloat { 62 | get { 63 | return slider.value 64 | } 65 | set { 66 | slider.setValue(value: newValue) 67 | textField.text = String(format: format, value) 68 | } 69 | } 70 | 71 | // The minimum value. The default value is 0.0. 72 | var minimumValue: CGFloat { 73 | get { 74 | return slider.minimumValue 75 | } 76 | set { 77 | slider.minimumValue = newValue 78 | } 79 | } 80 | 81 | // The maximum value. The default value is 255.0. 82 | var maximumValue: CGFloat { 83 | get { 84 | return slider.maximumValue 85 | } 86 | set { 87 | slider.maximumValue = newValue 88 | } 89 | } 90 | 91 | // The format string to use apply for textfield value. \c %.f by default. 92 | var format: String = "%.f" 93 | 94 | private let label: UILabel = { 95 | let label = UILabel() 96 | label.translatesAutoresizingMaskIntoConstraints = false 97 | label.adjustsFontSizeToFitWidth = true 98 | return label 99 | }() 100 | private let slider = EFSliderView() // The color slider to edit color component. 101 | private lazy var textField: UITextField = { 102 | let text = UITextField() 103 | text.borderStyle = .roundedRect 104 | text.translatesAutoresizingMaskIntoConstraints = false 105 | text.keyboardType = .numbersAndPunctuation 106 | text.isHidden = !colorTextFieldEnabled 107 | text.delegate = self 108 | return text 109 | }() 110 | 111 | override open class var requiresConstraintBasedLayout: Bool { 112 | return true 113 | } 114 | override init(frame: CGRect) { 115 | super.init(frame: frame) 116 | ef_baseInit() 117 | } 118 | 119 | required public init?(coder aDecoder: NSCoder) { 120 | super.init(coder: aDecoder) 121 | ef_baseInit() 122 | } 123 | 124 | // Sets the array of CGColorRef objects defining the color of each gradient stop on a slider's track. 125 | // The location of each gradient stop is evaluated with formula: i * width_of_the_track / number_of_colors. 126 | // @param colors An array of CGColorRef objects. 127 | func setColors(colors: [UIColor]) { 128 | if colors.count <= 1 { 129 | fatalError("‘colors: [CGColor]’ at least need to have 2 elements") 130 | } 131 | slider.setColors(colors: colors) 132 | } 133 | // MARK: - Private methods 134 | private func ef_baseInit() { 135 | accessibilityLabel = "color_component_view" 136 | addSubview(label) 137 | slider.maximumValue = EFRGBColorComponentMaxValue 138 | slider.translatesAutoresizingMaskIntoConstraints = false 139 | slider.addTarget(self, action: #selector(ef_didChangeSliderValue(sender:)), for: .valueChanged) 140 | addSubview(slider) 141 | addSubview(textField) 142 | value = 0.0 143 | self.ef_installConstraints() 144 | } 145 | 146 | @objc private func ef_didChangeSliderValue(sender: EFSliderView) { 147 | value = sender.value 148 | sendActions(for: .valueChanged) 149 | } 150 | 151 | private func ef_installConstraints() { 152 | if colorTextFieldEnabled { 153 | let views: [String: Any] = [ 154 | "label": label, 155 | "slider": slider, 156 | "textField": textField 157 | ] 158 | let metrics: [String: Any] = [ 159 | "spacing": EFColorComponentViewSpacing, 160 | "label_width": EFColorComponentLabelWidth, 161 | "textfield_width": EFColorComponentTextFieldWidth 162 | ] 163 | 164 | addConstraints( 165 | NSLayoutConstraint.constraints( 166 | withVisualFormat: "H:|[label(label_width)]-spacing-[slider]-spacing-[textField(textfield_width)]|", 167 | options: .alignAllCenterY, 168 | metrics: metrics, 169 | views: views 170 | ) 171 | ) 172 | addConstraints( 173 | NSLayoutConstraint.constraints( 174 | withVisualFormat: "V:|[label]|", 175 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 176 | metrics: nil, 177 | views: views 178 | ) 179 | ) 180 | addConstraints( 181 | NSLayoutConstraint.constraints( 182 | withVisualFormat: "V:|[textField]|", 183 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 184 | metrics: nil, 185 | views: views 186 | ) 187 | ) 188 | } else { 189 | let views: [String: Any] = [ 190 | "label": label, 191 | "slider": slider 192 | ] 193 | let metrics: [String: Any] = [ 194 | "spacing": EFColorComponentViewSpacing, 195 | "label_width": EFColorComponentLabelWidth 196 | ] 197 | addConstraints( 198 | NSLayoutConstraint.constraints( 199 | withVisualFormat: "H:|[label(label_width)]-spacing-[slider]-spacing-|", 200 | options: .alignAllCenterY, 201 | metrics: metrics, 202 | views: views 203 | ) 204 | ) 205 | addConstraints( 206 | NSLayoutConstraint.constraints( 207 | withVisualFormat: "V:|[label]|", 208 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 209 | metrics: nil, 210 | views: views 211 | ) 212 | ) 213 | } 214 | } 215 | 216 | private func ef_remakeConstraints() { 217 | // Remove all old constraints 218 | if !colorTextFieldEnabled { 219 | let views: [String: Any] = [ 220 | "label": label, 221 | "slider": slider, 222 | "textField": textField 223 | ] 224 | let metrics: [String: Any] = [ 225 | "spacing": EFColorComponentViewSpacing, 226 | "label_width": EFColorComponentLabelWidth, 227 | "textfield_width": EFColorComponentTextFieldWidth 228 | ] 229 | removeConstraints( 230 | NSLayoutConstraint.constraints( 231 | withVisualFormat: "H:|[label(label_width)]-spacing-[slider]-spacing-[textField(textfield_width)]|", 232 | options: .alignAllCenterY, 233 | metrics: metrics, 234 | views: views 235 | ) 236 | ) 237 | removeConstraints( 238 | NSLayoutConstraint.constraints( 239 | withVisualFormat: "V:|[label]|", 240 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 241 | metrics: nil, 242 | views: views 243 | ) 244 | ) 245 | removeConstraints( 246 | NSLayoutConstraint.constraints( 247 | withVisualFormat: "V:|[textField]|", 248 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 249 | metrics: nil, 250 | views: views 251 | ) 252 | ) 253 | } else { 254 | let views: [String: Any] = [ 255 | "label": label, 256 | "slider": slider 257 | ] 258 | let metrics: [String: Any] = [ 259 | "spacing": EFColorComponentViewSpacing, 260 | "label_width": EFColorComponentLabelWidth 261 | ] 262 | removeConstraints( 263 | NSLayoutConstraint.constraints( 264 | withVisualFormat: "H:|[label(label_width)]-spacing-[slider]-spacing-|", 265 | options: NSLayoutConstraint.FormatOptions.alignAllCenterY, 266 | metrics: metrics, 267 | views: views 268 | ) 269 | ) 270 | removeConstraints( 271 | NSLayoutConstraint.constraints( 272 | withVisualFormat: "V:|[label]|", 273 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 274 | metrics: nil, 275 | views: views 276 | ) 277 | ) 278 | } 279 | 280 | // Readd control 281 | for control in [label, slider, textField] { 282 | control.removeFromSuperview() 283 | self.addSubview(control) 284 | } 285 | 286 | // Add new constraints 287 | ef_installConstraints() 288 | } 289 | } 290 | extension EFColorComponentView: UITextFieldDelegate { 291 | // MARK: - UITextFieldDelegate methods 292 | public func textFieldDidEndEditing(_ textField: UITextField) { 293 | self.value = CGFloat(Double(textField.text ?? "") ?? 0) 294 | self.sendActions(for: .valueChanged) 295 | } 296 | 297 | public func textFieldShouldReturn(_ textField: UITextField) -> Bool { 298 | textField.resignFirstResponder() 299 | return true 300 | } 301 | 302 | public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 303 | let newString = NSString(string: textField.text ?? "").replacingCharacters(in: range, with: string) 304 | //first, check if the new string is numeric only. If not, return NO; 305 | let characterSet = NSCharacterSet(charactersIn: "0123456789,.").inverted 306 | if !(newString.rangeOfCharacter(from: characterSet)?.isEmpty != false) { 307 | return false 308 | } 309 | return CGFloat(Double(newString) ?? 0) <= slider.maximumValue 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFColorSelectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFColorSelectionView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/29. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import UIKit 29 | 30 | // The enum to define the EFColorView's types. 31 | public enum EFSelectedColorView: Int { 32 | // The RGB color view type. 33 | case RGB = 0 34 | 35 | // The HSB color view type. 36 | case HSB = 1 37 | } 38 | 39 | // The EFColorSelectionView aggregates views that should be used to edit color components. 40 | public class EFColorSelectionView: UIView, EFColorView, EFColorViewDelegate { 41 | 42 | // The selected color view 43 | private(set) var selectedIndex = EFSelectedColorView.RGB 44 | 45 | let rgbColorView = EFRGBView() 46 | let hsbColorView = EFHSBView() 47 | 48 | weak public var delegate: EFColorViewDelegate? 49 | var selectedView: EFColorView { 50 | return selectedIndex == .RGB ? rgbColorView : hsbColorView 51 | } 52 | public var color = UIColor.white { 53 | didSet { 54 | selectedView.color = color 55 | } 56 | } 57 | override init(frame: CGRect) { 58 | super.init(frame: frame) 59 | self.ef_init() 60 | } 61 | 62 | required public init?(coder aDecoder: NSCoder) { 63 | super.init(coder: aDecoder) 64 | self.ef_init() 65 | } 66 | 67 | // Makes a color component view (rgb or hsb) visible according to the index. 68 | // @param index This index define a view to show. 69 | // @param animated If YES, the view is being appeared using an animation. 70 | func setSelectedIndex(index: EFSelectedColorView, animated: Bool) { 71 | selectedIndex = index 72 | selectedView.color = color 73 | UIView.animate(withDuration: animated ? 0.5 : 0.0) { [weak self] in 74 | if let strongSelf = self { 75 | strongSelf.rgbColorView.alpha = .RGB == index ? 1.0 : 0.0 76 | strongSelf.hsbColorView.alpha = .HSB == index ? 1.0 : 0.0 77 | } 78 | } 79 | } 80 | 81 | func addColorView(view: EFColorView) { 82 | view.delegate = self 83 | guard let view = view as? UIView else { return } 84 | addSubview(view) 85 | view.translatesAutoresizingMaskIntoConstraints = false 86 | let views = [ 87 | "view": view 88 | ] 89 | let visualFormats = [ 90 | "H:|[view]|", 91 | "V:|[view]|" 92 | ] 93 | for visualFormat in visualFormats { 94 | addConstraints( 95 | NSLayoutConstraint.constraints( 96 | withVisualFormat: visualFormat, 97 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 98 | metrics: nil, 99 | views: views 100 | ) 101 | ) 102 | } 103 | } 104 | 105 | override public func updateConstraints() { 106 | self.rgbColorView.setNeedsUpdateConstraints() 107 | self.hsbColorView.setNeedsUpdateConstraints() 108 | super.updateConstraints() 109 | } 110 | 111 | // MARK: - FBColorViewDelegate methods 112 | public func colorView(_ colorView: EFColorView, didChangeColor color: UIColor) { 113 | self.color = color 114 | delegate?.colorView(self, didChangeColor: self.color) 115 | } 116 | 117 | // MARK: - Private 118 | private func ef_init() { 119 | if #available(iOS 11.0, *) { 120 | accessibilityIgnoresInvertColors = true 121 | } 122 | accessibilityLabel = "color_selection_view" 123 | 124 | if #available(iOS 13.0, *) { 125 | backgroundColor = .systemBackground 126 | } else { 127 | backgroundColor = .white 128 | } 129 | addColorView(view: rgbColorView) 130 | addColorView(view: hsbColorView) 131 | setSelectedIndex(index: .RGB, animated: false) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFColorSelectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFColorSelectionViewController.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/29. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | public enum EFColorSelectionMode: Int { 30 | case all = 0 31 | case rgb = 1 32 | case hsb = 2 33 | } 34 | 35 | // The delegate of a EFColorSelectionViewController object must adopt the EFColorSelectionViewController protocol. 36 | // Methods of the protocol allow the delegate to handle color value changes. 37 | @objc public protocol EFColorSelectionViewControllerDelegate: NSObjectProtocol { 38 | 39 | // Tells the data source to return the color components. 40 | // @param colorViewCntroller The color view. 41 | // @param color The new color value. 42 | func colorViewController(_ colorViewCntroller: EFColorSelectionViewController, didChangeColor color: UIColor) 43 | } 44 | 45 | public class EFColorSelectionViewController: UIViewController { 46 | 47 | // The controller's delegate. Controller notifies a delegate on color change. 48 | public weak var delegate: EFColorSelectionViewControllerDelegate? 49 | 50 | // The current color value. 51 | public var color: UIColor { 52 | get { 53 | return colorSelectionView.color 54 | } 55 | set { 56 | colorSelectionView.color = newValue 57 | } 58 | } 59 | let colorSelectionView = EFColorSelectionView(frame: UIScreen.main.bounds) 60 | let segmentControl = UISegmentedControl(items: [NSLocalizedString("RGB", comment: ""), NSLocalizedString("HSB", comment: "")]) 61 | // Whether colorTextField will hide, default is `true` 62 | public var isColorTextFieldHidden: Bool { 63 | get { 64 | return !colorSelectionView.hsbColorView.brightnessView.colorTextFieldEnabled 65 | } 66 | set { 67 | if colorSelectionView.hsbColorView.brightnessView.colorTextFieldEnabled == newValue { 68 | colorSelectionView.hsbColorView.brightnessView.colorTextFieldEnabled = !newValue 69 | for colorComponentView in colorSelectionView.rgbColorView.colorComponentViews { 70 | colorComponentView.colorTextFieldEnabled = !newValue 71 | } 72 | } 73 | } 74 | } 75 | override public func loadView() { 76 | self.view = colorSelectionView 77 | } 78 | 79 | override public func viewDidLoad() { 80 | super.viewDidLoad() 81 | segmentControl.addTarget( 82 | self, 83 | action: #selector(segmentControlDidChangeValue(_:)), 84 | for: .valueChanged 85 | ) 86 | segmentControl.selectedSegmentIndex = 0 87 | navigationItem.titleView = segmentControl 88 | 89 | colorSelectionView.setSelectedIndex(index: .RGB, animated: false) 90 | colorSelectionView.delegate = self 91 | edgesForExtendedLayout = UIRectEdge(rawValue: 0) 92 | } 93 | 94 | public func setMode(mode: EFColorSelectionMode) { 95 | switch mode { 96 | case .rgb: 97 | segmentControl.isHidden = true 98 | segmentControl.selectedSegmentIndex = 0 99 | colorSelectionView.setSelectedIndex(index: .RGB, animated: false) 100 | case .hsb: 101 | segmentControl.isHidden = true 102 | segmentControl.selectedSegmentIndex = 1 103 | colorSelectionView.setSelectedIndex(index: .HSB, animated: false) 104 | default: 105 | segmentControl.isHidden = false 106 | } 107 | } 108 | 109 | @IBAction func segmentControlDidChangeValue(_ segmentedControl: UISegmentedControl) { 110 | colorSelectionView.setSelectedIndex( 111 | index: EFSelectedColorView(rawValue: segmentedControl.selectedSegmentIndex) ?? .RGB, 112 | animated: true 113 | ) 114 | } 115 | 116 | override public func viewWillLayoutSubviews() { 117 | colorSelectionView.setNeedsUpdateConstraints() 118 | colorSelectionView.updateConstraintsIfNeeded() 119 | } 120 | } 121 | extension EFColorSelectionViewController: EFColorViewDelegate { 122 | public func colorView(_ colorView: EFColorView, didChangeColor color: UIColor) { 123 | delegate?.colorViewController(self, didChangeColor: color) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFColorUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFColorUtils.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | import CoreGraphics 29 | 30 | private let colorComponentValueRange = (CGFloat(0.0) ... CGFloat(1.0)) 31 | 32 | // The structure to represent a color in the Red-Green-Blue-Alpha color space. 33 | struct RGB { 34 | var red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat 35 | 36 | init(_ red: CGFloat, _ green: CGFloat, _ blue: CGFloat, _ alpha: CGFloat) { 37 | self.red = red 38 | self.green = green 39 | self.blue = blue 40 | self.alpha = alpha 41 | } 42 | } 43 | 44 | // The structure to represent a color in the hue-saturation-brightness color space. 45 | struct HSB { 46 | var hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat 47 | 48 | init(_ hue: CGFloat, _ saturation: CGFloat, _ brightness: CGFloat, _ alpha: CGFloat) { 49 | self.hue = hue 50 | self.saturation = saturation 51 | self.brightness = brightness 52 | self.alpha = alpha 53 | } 54 | } 55 | 56 | // The maximum value of the RGB color components. 57 | let EFRGBColorComponentMaxValue: CGFloat = 255.0 58 | 59 | // The maximum value of the alpha component. 60 | let EFAlphaComponentMaxValue: CGFloat = 100.0 61 | 62 | // The maximum value of the HSB color components. 63 | let EFHSBColorComponentMaxValue: CGFloat = 1.0 64 | 65 | // Converts an RGB color value to HSV. 66 | // Assumes r, g, and b are contained in the set [0, 1] and 67 | // returns h, s, and b in the set [0, 1]. 68 | // @param rgb The rgb color values 69 | // @return The hsb color values 70 | func EFRGB2HSB(rgb: RGB) -> HSB { 71 | let rd = Double(rgb.red) 72 | let gd = Double(rgb.green) 73 | let bd = Double(rgb.blue) 74 | let max = fmax(rd, fmax(gd, bd)) 75 | let min = fmin(rd, fmin(gd, bd)) 76 | var h = 0.0, b = max 77 | 78 | let d = max - min 79 | 80 | let s = max == 0 ? 0 : d / max 81 | 82 | if max == min { 83 | h = 0 // achromatic 84 | } else { 85 | if max == rd { 86 | h = (gd - bd) / d + (gd < bd ? 6 : 0) 87 | } else if max == gd { 88 | h = (bd - rd) / d + 2 89 | } else if max == bd { 90 | h = (rd - gd) / d + 4 91 | } 92 | 93 | h /= 6 94 | } 95 | 96 | return HSB(CGFloat(h), CGFloat(s), CGFloat(b), CGFloat(rgb.alpha)) 97 | } 98 | 99 | // Converts an HSB color value to RGB. 100 | // Assumes h, s, and b are contained in the set [0, 1] and 101 | // returns r, g, and b in the set [0, 255]. 102 | // @param outRGB The rgb color values 103 | // @return The hsb color values 104 | func EFHSB2RGB(hsb: HSB) -> RGB { 105 | var r: CGFloat = 0.0, g: CGFloat = 0.0, b: CGFloat = 0.0 106 | 107 | let i: Int = Int(hsb.hue * 6) 108 | let f = hsb.hue * 6 - CGFloat(i) 109 | let p = hsb.brightness * (1 - hsb.saturation) 110 | let q = hsb.brightness * (1 - f * hsb.saturation) 111 | let t = hsb.brightness * (1 - (1 - f) * hsb.saturation) 112 | 113 | switch i % 6 { 114 | case 0: 115 | r = hsb.brightness 116 | g = t 117 | b = p 118 | case 1: 119 | r = q 120 | g = hsb.brightness 121 | b = p 122 | case 2: 123 | r = p 124 | g = hsb.brightness 125 | b = t 126 | case 3: 127 | r = p 128 | g = q 129 | b = hsb.brightness 130 | case 4: 131 | r = t 132 | g = p 133 | b = hsb.brightness 134 | case 5: 135 | r = hsb.brightness 136 | g = p 137 | b = q 138 | default: 139 | break 140 | } 141 | return RGB(r, g, b, hsb.alpha) 142 | } 143 | 144 | // Returns the rgb values of the color components. 145 | // @param color The color value. 146 | // @return The values of the color components (including alpha). 147 | func EFRGBColorComponents(color: UIColor) -> RGB { 148 | var result = RGB(1, 1, 1, 1) 149 | guard let colorSpaceModel = color.cgColor.colorSpace?.model else { 150 | return result 151 | } 152 | 153 | if .rgb != colorSpaceModel && .monochrome != colorSpaceModel { 154 | return result 155 | } 156 | 157 | guard let components = color.cgColor.components else { 158 | return result 159 | } 160 | 161 | if .monochrome == colorSpaceModel { 162 | result.red = components[0].clamped(to: colorComponentValueRange) 163 | result.green = components[0].clamped(to: colorComponentValueRange) 164 | result.blue = components[0].clamped(to: colorComponentValueRange) 165 | result.alpha = components[1].clamped(to: colorComponentValueRange) 166 | } else { 167 | result.red = components[0].clamped(to: colorComponentValueRange) 168 | result.green = components[1].clamped(to: colorComponentValueRange) 169 | result.blue = components[2].clamped(to: colorComponentValueRange) 170 | result.alpha = components[3].clamped(to: colorComponentValueRange) 171 | } 172 | 173 | return result 174 | } 175 | 176 | // Converts hex string to the UIColor representation. 177 | // @param color The color value. 178 | // @return The hex string color value. 179 | func EFHexStringFromColor(color: UIColor) -> String? { 180 | guard let colorSpaceModel = color.cgColor.colorSpace?.model else { 181 | return nil 182 | } 183 | 184 | if .rgb != colorSpaceModel && .monochrome != colorSpaceModel { 185 | return nil 186 | } 187 | 188 | guard let components = color.cgColor.components else { 189 | return nil 190 | } 191 | var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 192 | 193 | if .monochrome == colorSpaceModel { 194 | red = components[0].clamped(to: colorComponentValueRange) 195 | green = components[0].clamped(to: colorComponentValueRange) 196 | blue = components[0].clamped(to: colorComponentValueRange) 197 | alpha = components[1].clamped(to: colorComponentValueRange) 198 | } else { 199 | red = components[0].clamped(to: colorComponentValueRange) 200 | green = components[1].clamped(to: colorComponentValueRange) 201 | blue = components[2].clamped(to: colorComponentValueRange) 202 | alpha = components[3].clamped(to: colorComponentValueRange) 203 | } 204 | 205 | return String( 206 | format: "#%02lX%02lX%02lX%02lX", 207 | UInt64(red * EFRGBColorComponentMaxValue), 208 | UInt64(green * EFRGBColorComponentMaxValue), 209 | UInt64(blue * EFRGBColorComponentMaxValue), 210 | UInt64(alpha * EFRGBColorComponentMaxValue) 211 | ) 212 | } 213 | 214 | // Converts UIColor value to the hex string. 215 | // @param hexString The hex string color value. 216 | // @return The color value. 217 | func EFColorFromHexString(hexColor: String) -> UIColor? { 218 | if !hexColor.hasPrefix("#") { 219 | return nil 220 | } 221 | 222 | let scanner = Scanner(string: hexColor) 223 | scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#") 224 | 225 | var hexNum: UInt32 = 0 226 | if !scanner.scanHexInt32(&hexNum) { 227 | return nil 228 | } 229 | 230 | let r: CGFloat = CGFloat((hexNum >> 24) & 0xFF) 231 | let g: CGFloat = CGFloat((hexNum >> 16) & 0xFF) 232 | let b: CGFloat = CGFloat((hexNum >> 8) & 0xFF) 233 | let a: CGFloat = CGFloat((hexNum) & 0xFF) 234 | 235 | return UIColor( 236 | red: r / EFRGBColorComponentMaxValue, 237 | green: g / EFRGBColorComponentMaxValue, 238 | blue: b / EFRGBColorComponentMaxValue, 239 | alpha: a / EFRGBColorComponentMaxValue 240 | ) 241 | } 242 | 243 | extension Comparable { 244 | func clamped(to limits: ClosedRange) -> Self { 245 | return min(max(self, limits.lowerBound), limits.upperBound) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFColorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFColorView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | // The delegate of a EFColorView object must adopt the EFColorViewDelegate protocol. 30 | // Methods of the protocol allow the delegate to handle color value changes. 31 | public protocol EFColorViewDelegate: class { 32 | 33 | // Tells the data source to return the color components. 34 | // @param colorView The color view. 35 | // @param color The new color value. 36 | func colorView(_ colorView: EFColorView, didChangeColor color: UIColor) 37 | } 38 | 39 | /// The \c EFColorView protocol declares a view's interface for displaying and editing color value. 40 | public protocol EFColorView: class { 41 | 42 | // The object that acts as the delegate of the receiving color selection view. 43 | var delegate: EFColorViewDelegate? { get set } 44 | 45 | // The current color. 46 | var color: UIColor { get set } 47 | } 48 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFColorWheelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFColorWheelView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/29. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | import CoreGraphics 29 | 30 | // The color wheel view. 31 | public class EFColorWheelView: UIControl { 32 | 33 | public var isTouched = false 34 | var wheelImage: CGImage? 35 | 36 | // The hue value. 37 | public var hue: CGFloat = 0.0 { 38 | didSet { 39 | setSelectedPoint(point: ef_selectedPoint()) 40 | setNeedsDisplay() 41 | } 42 | } 43 | 44 | // The saturation value. 45 | public var saturation: CGFloat = 0.0 { 46 | didSet { 47 | setSelectedPoint(point: ef_selectedPoint()) 48 | setNeedsDisplay() 49 | } 50 | } 51 | 52 | // The saturation value. 53 | public var brightness: CGFloat = 1.0 { 54 | didSet { 55 | self.setSelectedPoint(point: ef_selectedPoint()) 56 | if oldValue != brightness { 57 | drawWheelImage() 58 | } 59 | } 60 | } 61 | 62 | private lazy var indicatorLayer: CALayer = { 63 | let dimension: CGFloat = 33 64 | let edgeColor = UIColor(white: 0.9, alpha: 0.8) 65 | 66 | let indicatorLayer = CALayer() 67 | indicatorLayer.cornerRadius = dimension / 2 68 | indicatorLayer.borderColor = edgeColor.cgColor 69 | indicatorLayer.borderWidth = 2 70 | indicatorLayer.backgroundColor = UIColor.white.cgColor 71 | indicatorLayer.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension) 72 | indicatorLayer.position = CGPoint(x: self.bounds.width / 2, y: self.bounds.height / 2) 73 | indicatorLayer.shadowColor = UIColor.black.cgColor 74 | indicatorLayer.shadowOffset = CGSize.zero 75 | indicatorLayer.shadowRadius = 1 76 | indicatorLayer.shadowOpacity = 0.5 77 | return indicatorLayer 78 | }() 79 | 80 | override open class var requiresConstraintBasedLayout: Bool { 81 | return true 82 | } 83 | 84 | override init(frame: CGRect) { 85 | super.init(frame: frame) 86 | 87 | accessibilityLabel = "color_wheel_view" 88 | layer.delegate = self 89 | layer.addSublayer(indicatorLayer) 90 | 91 | // [self setSelectedPoint:CGPointMake(dimension / 2, dimension / 2)]; 92 | } 93 | 94 | required public init?(coder aDecoder: NSCoder) { 95 | fatalError("init(coder:) has not been implemented") 96 | } 97 | 98 | override public func touchesBegan(_ touches: Set, with event: UIEvent?) { 99 | isTouched = true 100 | if let position = touches.first?.location(in: self) { 101 | onTouchEventWithPosition(point: position) 102 | } 103 | } 104 | 105 | override public func touchesMoved(_ touches: Set, with event: UIEvent?) { 106 | if let position = touches.first?.location(in: self) { 107 | onTouchEventWithPosition(point: position) 108 | } 109 | } 110 | 111 | override public func touchesEnded(_ touches: Set, with event: UIEvent?) { 112 | isTouched = false 113 | if let position = touches.first?.location(in: self) { 114 | onTouchEventWithPosition(point: position) 115 | } 116 | } 117 | 118 | func onTouchEventWithPosition(point: CGPoint) { 119 | let radius = self.bounds.width / 2 120 | 121 | let mx = Double(radius - point.x) 122 | let my = Double(radius - point.y) 123 | let dist = CGFloat(sqrt(mx * mx + my * my)) 124 | 125 | if dist <= radius { 126 | var h = hue 127 | var s = saturation 128 | ef_colorWheelValueWithPosition(position: point, hue: &h, saturation: &s) 129 | hue = h 130 | saturation = s 131 | setSelectedPoint(point: point) 132 | sendActions(for: .valueChanged) 133 | } 134 | } 135 | 136 | func setSelectedPoint(point: CGPoint) { 137 | let selectedColor = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: 1) 138 | 139 | CATransaction.begin() 140 | CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) 141 | self.indicatorLayer.position = point 142 | self.indicatorLayer.backgroundColor = selectedColor.cgColor 143 | CATransaction.commit() 144 | } 145 | 146 | // MARK: - CALayerDelegate methods 147 | override public func display(_ layer: CALayer) { 148 | guard wheelImage == nil else { return } 149 | drawWheelImage() 150 | } 151 | 152 | override public func layoutSublayers(of layer: CALayer) { 153 | if layer == self.layer { 154 | self.setSelectedPoint(point: self.ef_selectedPoint()) 155 | self.layer.setNeedsDisplay() 156 | } 157 | } 158 | 159 | // MARK: - Private methods 160 | private func drawWheelImage() { 161 | let dimension: CGFloat = min(self.frame.width, self.frame.height) 162 | guard let bitmapData = CFDataCreateMutable(nil, 0) else { 163 | return 164 | } 165 | 166 | CFDataSetLength(bitmapData, CFIndex(dimension * dimension * 4)) 167 | self.ef_colorWheelBitmap( 168 | bitmap: CFDataGetMutableBytePtr(bitmapData), 169 | withSize: CGSize(width: dimension, height: dimension) 170 | ) 171 | if let image = self.ef_imageWithRGBAData(data: bitmapData, width: Int(dimension), height: Int(dimension)) { 172 | wheelImage = image 173 | self.layer.contents = wheelImage 174 | } 175 | } 176 | 177 | private func ef_selectedPoint() -> CGPoint { 178 | let dimension = min(frame.width, frame.height) 179 | 180 | let radius = saturation * dimension / 2 181 | let x = dimension / 2 + radius * CGFloat(cos(Double(hue) * Double.pi * 2.0)) 182 | let y = dimension / 2 + radius * CGFloat(sin(Double(hue) * Double.pi * 2.0)) 183 | 184 | return CGPoint(x: x, y: y) 185 | } 186 | 187 | private func ef_colorWheelBitmap(bitmap: UnsafeMutablePointer?, withSize size: CGSize) { 188 | if size.width <= 0 || size.height <= 0 { 189 | return 190 | } 191 | 192 | for y in 0 ..< Int(size.width) { 193 | for x in 0 ..< Int(size.height) { 194 | var hue: CGFloat = 0, saturation: CGFloat = 0, a: CGFloat = 0.0 195 | var h = hue 196 | var s = saturation 197 | ef_colorWheelValueWithPosition(position: CGPoint(x: x, y: y), hue: &h, saturation: &s) 198 | hue = h 199 | saturation = s 200 | 201 | var rgb = RGB(1, 1, 1, 1) 202 | if saturation < 1.0 { 203 | // Antialias the edge of the circle. 204 | if saturation > 0.99 { 205 | a = (1.0 - saturation) * 100 206 | } else { 207 | a = 1.0 208 | } 209 | 210 | let hsb = HSB(hue, saturation, brightness, a) 211 | rgb = EFHSB2RGB(hsb: hsb) 212 | } 213 | 214 | let i = 4 * (x + y * Int(size.width)) 215 | bitmap?[i] = UInt8(rgb.red * 0xff) 216 | bitmap?[i + 1] = UInt8(rgb.green * 0xff) 217 | bitmap?[i + 2] = UInt8(rgb.blue * 0xff) 218 | bitmap?[i + 3] = UInt8(rgb.alpha * 0xff) 219 | } 220 | } 221 | } 222 | 223 | private func ef_colorWheelValueWithPosition(position: CGPoint, hue: inout CGFloat, saturation: inout CGFloat) { 224 | let c = Int(bounds.width / 2) 225 | let dx = (position.x - CGFloat(c)) / CGFloat(c) 226 | let dy = (position.y - CGFloat(c)) / CGFloat(c) 227 | let d = CGFloat(sqrt(Double(dx * dx + dy * dy))) 228 | 229 | saturation = d 230 | 231 | if d == 0 { 232 | hue = 0 233 | } else { 234 | hue = acos(dx / d) / CGFloat.pi / 2.0 235 | 236 | if dy < 0 { 237 | hue = 1.0 - hue 238 | } 239 | } 240 | } 241 | 242 | private func ef_imageWithRGBAData(data: CFData, width: Int, height: Int) -> CGImage? { 243 | guard let dataProvider = CGDataProvider(data: data) else { 244 | return nil 245 | } 246 | let colorSpace = CGColorSpaceCreateDeviceRGB() 247 | 248 | let imageRef = CGImage( 249 | width: width, 250 | height: height, 251 | bitsPerComponent: 8, 252 | bitsPerPixel: 32, 253 | bytesPerRow: width * 4, 254 | space: colorSpace, 255 | bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue), 256 | provider: dataProvider, 257 | decode: nil, 258 | shouldInterpolate: false, 259 | intent: CGColorRenderingIntent.defaultIntent 260 | ) 261 | return imageRef 262 | } 263 | } 264 | 265 | // MARK: - Fix iOS 13 popover style doesn't play well with the Color Wheel 266 | // https://github.com/EFPrefix/EFColorPicker/issues/25 267 | extension EFColorWheelView{ 268 | override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { 269 | if gestureRecognizer is UIPanGestureRecognizer{ 270 | if let hsbView = self.superview as? EFHSBView{ 271 | return !hsbView.isTouched 272 | } 273 | } 274 | return true 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFControl.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFControl.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | public class EFControl: UIControl { 30 | 31 | // Edge inset values are applied to a view bounds to shrink or expand the touchable area. 32 | var hitTestEdgeInsets = UIEdgeInsets.zero 33 | 34 | override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 35 | if self.hitTestEdgeInsets == .zero 36 | || !self.isEnabled 37 | || self.isHidden 38 | || !self.isUserInteractionEnabled 39 | || 0 == self.alpha { 40 | return super.point(inside: point, with: event) 41 | } 42 | 43 | let hitFrame: CGRect = self.bounds.inset(by: self.hitTestEdgeInsets) 44 | return hitFrame.contains(point) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFHSBView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFHSBView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/29. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | // The view to edit HSB color components. 30 | public class EFHSBView: UIView, EFColorView, UITextFieldDelegate { 31 | 32 | let EFColorSampleViewHeight: CGFloat = 30.0 33 | let EFViewMargin: CGFloat = 20.0 34 | let EFColorWheelDimension: CGFloat = 200.0 35 | 36 | private let colorWheel = EFColorWheelView() 37 | public let brightnessView: EFColorComponentView = { 38 | let view = EFColorComponentView() 39 | view.title = NSLocalizedString("Brightness", comment: "") 40 | view.maximumValue = EFHSBColorComponentMaxValue 41 | view.format = "%.2f" 42 | view.translatesAutoresizingMaskIntoConstraints = false 43 | view.setColors(colors: [.black, .white]) 44 | return view 45 | }() 46 | private let colorSample: UIView = { 47 | let view = UIView() 48 | view.accessibilityLabel = "color_sample" 49 | view.layer.borderColor = UIColor.black.cgColor 50 | view.layer.borderWidth = 0.5 51 | view.translatesAutoresizingMaskIntoConstraints = false 52 | return view 53 | }() 54 | private var colorComponents = HSB(1, 1, 1, 1) 55 | private var layoutConstraints: [NSLayoutConstraint] = [] 56 | 57 | weak public var delegate: EFColorViewDelegate? 58 | 59 | public var isTouched: Bool { 60 | if self.colorWheel.isTouched { 61 | return true 62 | } 63 | if self.brightnessView.isTouched { 64 | return true 65 | } 66 | return false 67 | } 68 | 69 | public var color: UIColor { 70 | get { 71 | return UIColor( 72 | hue: colorComponents.hue, 73 | saturation: colorComponents.saturation, 74 | brightness: colorComponents.brightness, 75 | alpha: colorComponents.alpha 76 | ) 77 | } 78 | set { 79 | colorComponents = EFRGB2HSB(rgb: EFRGBColorComponents(color: newValue)) 80 | self.reloadData() 81 | } 82 | } 83 | 84 | override init(frame: CGRect) { 85 | super.init(frame: frame) 86 | self.ef_baseInit() 87 | } 88 | 89 | required public init?(coder aDecoder: NSCoder) { 90 | super.init(coder: aDecoder) 91 | self.ef_baseInit() 92 | } 93 | 94 | func reloadData() { 95 | colorSample.backgroundColor = color 96 | colorSample.accessibilityValue = EFHexStringFromColor(color: color) 97 | self.ef_reloadViewsWithColorComponents(colorComponents: colorComponents) 98 | self.colorWheel.display(colorWheel.layer) 99 | } 100 | override public func updateConstraints() { 101 | self.ef_updateConstraints() 102 | super.updateConstraints() 103 | } 104 | 105 | // MARK: - Private methods 106 | private func ef_baseInit() { 107 | accessibilityLabel = "hsb_view" 108 | addSubview(colorSample) 109 | 110 | colorWheel.translatesAutoresizingMaskIntoConstraints = false 111 | addSubview(colorWheel) 112 | addSubview(brightnessView) 113 | 114 | colorWheel.addTarget( 115 | self, action: #selector(ef_colorDidChangeValue(sender:)), for: UIControl.Event.valueChanged 116 | ) 117 | brightnessView.addTarget( 118 | self, action: #selector(ef_brightnessDidChangeValue(sender:)), for: UIControl.Event.valueChanged 119 | ) 120 | setNeedsUpdateConstraints() 121 | } 122 | 123 | private func ef_updateConstraints() { 124 | // remove all constraints first 125 | if !layoutConstraints.isEmpty { 126 | removeConstraints(layoutConstraints) 127 | } 128 | 129 | layoutConstraints = UIUserInterfaceSizeClass.compact == traitCollection.verticalSizeClass 130 | ? ef_constraintsForCompactVerticalSizeClass() 131 | : ef_constraintsForRegularVerticalSizeClass() 132 | addConstraints(layoutConstraints) 133 | } 134 | 135 | private func ef_constraintsForRegularVerticalSizeClass() -> [NSLayoutConstraint] { 136 | let metrics = [ 137 | "margin": EFViewMargin, 138 | "height": EFColorSampleViewHeight, 139 | "color_wheel_dimension": EFColorWheelDimension 140 | ] 141 | let views = [ 142 | "colorSample": colorSample, 143 | "colorWheel": colorWheel, 144 | "brightnessView": brightnessView 145 | ] 146 | 147 | var layoutConstraints: [NSLayoutConstraint] = [] 148 | let visualFormats = [ 149 | "H:|-margin-[colorSample]-margin-|", 150 | "H:|-margin-[colorWheel(>=color_wheel_dimension)]-margin-|", 151 | "H:|-margin-[brightnessView]-margin-|", 152 | "V:|-margin-[colorSample(height)]-margin-[colorWheel]-margin-[brightnessView]-(>=margin@250)-|" 153 | ] 154 | for visualFormat in visualFormats { 155 | layoutConstraints.append( 156 | contentsOf: NSLayoutConstraint.constraints( 157 | withVisualFormat: visualFormat, 158 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 159 | metrics: metrics, 160 | views: views 161 | ) 162 | ) 163 | } 164 | layoutConstraints.append( 165 | NSLayoutConstraint( 166 | item: colorWheel, 167 | attribute: .width, 168 | relatedBy: .equal, 169 | toItem: colorWheel, 170 | attribute: .height, 171 | multiplier: 1, 172 | constant: 0) 173 | ) 174 | return layoutConstraints 175 | } 176 | private func ef_constraintsForCompactVerticalSizeClass() -> [NSLayoutConstraint] { 177 | let metrics = [ 178 | "margin": EFViewMargin, 179 | "height": EFColorSampleViewHeight, 180 | "color_wheel_dimension": EFColorWheelDimension 181 | ] 182 | let views = [ 183 | "colorSample": colorSample, 184 | "colorWheel": colorWheel, 185 | "brightnessView": brightnessView 186 | ] 187 | 188 | var layoutConstraints: [NSLayoutConstraint] = [] 189 | let visualFormats = [ 190 | "H:|-margin-[colorSample]-margin-|", 191 | "H:|-margin-[colorWheel(>=color_wheel_dimension)]-margin-[brightnessView]-(margin@500)-|", 192 | "V:|-margin-[colorSample(height)]-margin-[colorWheel]-(margin@500)-|" 193 | ] 194 | for visualFormat in visualFormats { 195 | layoutConstraints.append( 196 | contentsOf: NSLayoutConstraint.constraints( 197 | withVisualFormat: visualFormat, 198 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 199 | metrics: metrics, 200 | views: views 201 | ) 202 | ) 203 | } 204 | layoutConstraints.append( 205 | NSLayoutConstraint( 206 | item: colorWheel, 207 | attribute: .width, 208 | relatedBy: .equal, 209 | toItem: colorWheel, 210 | attribute: .height, 211 | multiplier: 1, 212 | constant: 0) 213 | ) 214 | layoutConstraints.append( 215 | NSLayoutConstraint( 216 | item: brightnessView, 217 | attribute: .centerY, 218 | relatedBy: .equal, 219 | toItem: self, 220 | attribute: .centerY, 221 | multiplier: 1, 222 | constant: 0) 223 | ) 224 | return layoutConstraints 225 | } 226 | 227 | private func ef_reloadViewsWithColorComponents(colorComponents: HSB) { 228 | colorWheel.hue = colorComponents.hue 229 | colorWheel.saturation = colorComponents.saturation 230 | colorWheel.brightness = colorComponents.brightness 231 | self.ef_updateSlidersWithColorComponents(colorComponents: colorComponents) 232 | } 233 | 234 | private func ef_updateSlidersWithColorComponents(colorComponents: HSB) { 235 | brightnessView.value = colorComponents.brightness 236 | } 237 | 238 | @objc private func ef_colorDidChangeValue(sender: EFColorWheelView) { 239 | colorComponents.hue = sender.hue 240 | colorComponents.saturation = sender.saturation 241 | self.delegate?.colorView(self, didChangeColor: self.color) 242 | self.reloadData() 243 | } 244 | 245 | @objc private func ef_brightnessDidChangeValue(sender: EFColorComponentView) { 246 | colorComponents.brightness = sender.value 247 | self.colorWheel.brightness = sender.value 248 | self.delegate?.colorView(self, didChangeColor: self.color) 249 | self.reloadData() 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFRGBView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFRGBView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/29. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | public class EFRGBView: UIView, EFColorView { 30 | 31 | let EFColorSampleViewHeight: CGFloat = 30.0 32 | let EFViewMargin: CGFloat = 20.0 33 | let EFSliderViewMargin: CGFloat = 30.0 34 | let EFRGBColorComponentsSize: Int = 3 35 | 36 | private let colorSample: UIView = { 37 | let view = UIView() 38 | view.accessibilityLabel = "color_sample" 39 | view.layer.borderColor = UIColor.black.cgColor 40 | view.layer.borderWidth = 0.5 41 | view.translatesAutoresizingMaskIntoConstraints = false 42 | return view 43 | }() 44 | var colorComponentViews: [EFColorComponentView] = [] 45 | private var colorComponents = RGB(1, 1, 1, 1) 46 | 47 | weak public var delegate: EFColorViewDelegate? 48 | 49 | public var isTouched: Bool { 50 | return colorComponentViews.filter { $0.isTouched }.count > 0 51 | } 52 | 53 | public var color: UIColor { 54 | get { 55 | return UIColor( 56 | red: colorComponents.red, 57 | green: colorComponents.green, 58 | blue: colorComponents.blue, 59 | alpha: colorComponents.alpha 60 | ) 61 | } 62 | set { 63 | colorComponents = EFRGBColorComponents(color: newValue) 64 | reloadData() 65 | } 66 | } 67 | 68 | override init(frame: CGRect) { 69 | super.init(frame: frame) 70 | ef_baseInit() 71 | } 72 | 73 | required public init?(coder aDecoder: NSCoder) { 74 | super.init(coder: aDecoder) 75 | ef_baseInit() 76 | } 77 | 78 | func reloadData() { 79 | colorSample.backgroundColor = color 80 | colorSample.accessibilityValue = EFHexStringFromColor(color: self.color) 81 | ef_reloadColorComponentViews(colorComponents: colorComponents) 82 | } 83 | 84 | // MARK: - Private methods 85 | private func ef_baseInit() { 86 | accessibilityLabel = "rgb_view" 87 | addSubview(colorSample) 88 | 89 | var tmp: [EFColorComponentView] = [] 90 | let titles = [ 91 | NSLocalizedString("Red", comment: ""), 92 | NSLocalizedString("Green", comment: ""), 93 | NSLocalizedString("Blue", comment: "") 94 | ] 95 | let maxValues = [ 96 | EFRGBColorComponentMaxValue, EFRGBColorComponentMaxValue, EFRGBColorComponentMaxValue 97 | ] 98 | for i in 0 ..< EFRGBColorComponentsSize { 99 | let colorComponentView = ef_colorComponentViewWithTitle( 100 | title: titles[i], tag: i, maxValue: maxValues[i] 101 | ) 102 | addSubview(colorComponentView) 103 | colorComponentView.addTarget( 104 | self, action: #selector(ef_colorComponentDidChangeValue(_:)), for: UIControl.Event.valueChanged 105 | ) 106 | tmp.append(colorComponentView) 107 | } 108 | 109 | colorComponentViews = tmp 110 | ef_installConstraints() 111 | } 112 | 113 | @objc @IBAction private func ef_colorComponentDidChangeValue(_ sender: EFColorComponentView) { 114 | self.ef_setColorComponentValue(value: sender.value / sender.maximumValue, atIndex: UInt(sender.tag)) 115 | self.delegate?.colorView(self, didChangeColor: self.color) 116 | self.reloadData() 117 | } 118 | 119 | private func ef_setColorComponentValue(value: CGFloat, atIndex index: UInt) { 120 | switch index { 121 | case 0: 122 | colorComponents.red = value 123 | case 1: 124 | colorComponents.green = value 125 | case 2: 126 | colorComponents.blue = value 127 | default: 128 | colorComponents.alpha = value 129 | } 130 | } 131 | 132 | private func ef_colorComponentViewWithTitle(title: String, tag: Int, maxValue: CGFloat) -> EFColorComponentView { 133 | let colorComponentView = EFColorComponentView() 134 | colorComponentView.title = title 135 | colorComponentView.translatesAutoresizingMaskIntoConstraints = false 136 | colorComponentView.tag = tag 137 | colorComponentView.maximumValue = maxValue 138 | return colorComponentView 139 | } 140 | 141 | private func ef_installConstraints() { 142 | let metrics = [ 143 | "margin": EFViewMargin, 144 | "height": EFColorSampleViewHeight, 145 | "slider_margin": EFSliderViewMargin 146 | ] 147 | var views = [ 148 | "colorSample": colorSample 149 | ] 150 | 151 | let visualFormats = [ 152 | "H:|-margin-[colorSample]-margin-|", 153 | "V:|-margin-[colorSample(height)]" 154 | ] 155 | for visualFormat in visualFormats { 156 | addConstraints( 157 | NSLayoutConstraint.constraints( 158 | withVisualFormat: visualFormat, 159 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 160 | metrics: metrics, 161 | views: views 162 | ) 163 | ) 164 | } 165 | 166 | var previousView = colorSample 167 | for colorComponentView in colorComponentViews { 168 | views = [ 169 | "previousView": previousView, 170 | "colorComponentView": colorComponentView 171 | ] 172 | 173 | let visualFormats = [ 174 | "H:|-margin-[colorComponentView]-margin-|", 175 | "V:[previousView]-slider_margin-[colorComponentView]" 176 | ] 177 | for visualFormat in visualFormats { 178 | self.addConstraints( 179 | NSLayoutConstraint.constraints( 180 | withVisualFormat: visualFormat, 181 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 182 | metrics: metrics, 183 | views: views 184 | ) 185 | ) 186 | } 187 | 188 | previousView = colorComponentView 189 | } 190 | 191 | views = [ 192 | "previousView": previousView 193 | ] 194 | addConstraints( 195 | NSLayoutConstraint.constraints( 196 | withVisualFormat: "V:[previousView]-(>=margin)-|", 197 | options: NSLayoutConstraint.FormatOptions(rawValue: 0), 198 | metrics: metrics, 199 | views: views 200 | ) 201 | ) 202 | } 203 | 204 | private func ef_colorComponentsWithRGB(rgb: RGB) -> [CGFloat] { 205 | return [rgb.red, rgb.green, rgb.blue, rgb.alpha] 206 | } 207 | 208 | private func ef_reloadColorComponentViews(colorComponents: RGB) { 209 | let components = ef_colorComponentsWithRGB(rgb: colorComponents) 210 | 211 | for (idx, colorComponentView) in colorComponentViews.enumerated() { 212 | let cgColors = self.ef_colorsWithColorComponents(colorComponents: components, currentColorIndex: colorComponentView.tag) 213 | let colors = cgColors.map { UIColor(cgColor: $0) } 214 | 215 | colorComponentView.setColors(colors: colors) 216 | colorComponentView.value = components[idx] * colorComponentView.maximumValue 217 | } 218 | } 219 | 220 | private func ef_colorsWithColorComponents(colorComponents: [CGFloat], currentColorIndex colorIndex: Int) -> [CGColor] { 221 | let currentColorValue = colorComponents[colorIndex] 222 | var colors = [CGFloat](repeating: 0, count: 12) 223 | for i in 0 ..< EFRGBColorComponentsSize { 224 | colors[i] = colorComponents[i] 225 | colors[i + 4] = colorComponents[i] 226 | colors[i + 8] = colorComponents[i] 227 | } 228 | colors[colorIndex] = 0 229 | colors[colorIndex + 4] = currentColorValue 230 | colors[colorIndex + 8] = 1.0 231 | 232 | let start = UIColor(red: colors[0], green: colors[1], blue: colors[2], alpha: 1) 233 | let middle = UIColor(red: colors[4], green: colors[5], blue: colors[6], alpha: 1) 234 | let end = UIColor(red: colors[8], green: colors[9], blue: colors[10], alpha: 1) 235 | 236 | return [start.cgColor, middle.cgColor, end.cgColor] 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFSliderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFSliderView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import CoreGraphics 29 | import QuartzCore 30 | import UIKit 31 | 32 | public class EFSliderView: EFControl { 33 | 34 | let EFSliderViewHeight: CGFloat = 28.0 35 | let EFSliderViewMinWidth: CGFloat = 150.0 36 | let EFSliderViewTrackHeight: CGFloat = 6.0 37 | let EFThumbViewEdgeInset: CGFloat = -10.0 38 | 39 | private lazy var thumbView: EFThumbView = { 40 | let thumb = EFThumbView() 41 | thumb.hitTestEdgeInsets = UIEdgeInsets( 42 | top: EFThumbViewEdgeInset, left: EFThumbViewEdgeInset, 43 | bottom: EFThumbViewEdgeInset, right: EFThumbViewEdgeInset 44 | ) 45 | thumb.gestureRecognizer.addTarget(self, action: #selector(ef_didPanThumbView(gestureRecognizer:))) 46 | return thumb 47 | }() 48 | private lazy var trackLayer: CAGradientLayer = { 49 | let gradient = CAGradientLayer() 50 | gradient.cornerRadius = EFSliderViewTrackHeight / 2.0 51 | gradient.startPoint = CGPoint(x: 0.0, y: 0.5) 52 | gradient.endPoint = CGPoint(x: 1.0, y: 0.5) 53 | return gradient 54 | }() 55 | 56 | // The slider's current value. The default value is 0.0. 57 | public private(set) var value: CGFloat = 0 58 | 59 | // The minimum value of the slider. The default value is 0.0. 60 | public var minimumValue: CGFloat = 0 61 | 62 | // The maximum value of the slider. The default value is 1.0. 63 | public var maximumValue: CGFloat = 1 64 | 65 | // Indicates if the user touches the control at the moment 66 | public var isTouched = false 67 | 68 | override init(frame: CGRect) { 69 | super.init(frame: frame) 70 | accessibilityLabel = "color_slider" 71 | layer.delegate = self 72 | layer.addSublayer(trackLayer) 73 | addSubview(thumbView) 74 | setColors(colors: [.blue, .blue]) 75 | } 76 | 77 | required public init?(coder aDecoder: NSCoder) { 78 | fatalError("init(coder:) has not been implemented") 79 | } 80 | 81 | override open class var requiresConstraintBasedLayout: Bool { 82 | return true 83 | } 84 | 85 | override public var intrinsicContentSize: CGSize { 86 | return CGSize(width: EFSliderViewMinWidth, height: EFSliderViewHeight) 87 | } 88 | 89 | func setValue(value: CGFloat) { 90 | self.value = min(maximumValue, max(value, minimumValue)) 91 | self.ef_updateThumbPositionWithValue(value: self.value) 92 | } 93 | 94 | // Sets the array of CGColorRef objects defining the color of each gradient stop on the track. 95 | // The location of each gradient stop is evaluated with formula: i * width_of_the_track / number_of_colors. 96 | // @param colors An array of CGColorRef objects. 97 | func setColors(colors: [UIColor]) { 98 | let cgColors = colors.map {$0.cgColor} 99 | if cgColors.count <= 1 { 100 | fatalError("‘colors: [CGColor]’ at least need to have 2 elements") 101 | } 102 | trackLayer.colors = cgColors 103 | self.ef_updateLocations() 104 | } 105 | override public func layoutSubviews() { 106 | self.ef_updateThumbPositionWithValue(value: self.value) 107 | self.ef_updateTrackLayer() 108 | } 109 | // MARK: - UIControl touch tracking events 110 | @objc func ef_didPanThumbView(gestureRecognizer: UIPanGestureRecognizer) { 111 | if gestureRecognizer.state == UIGestureRecognizer.State.ended { 112 | self.isTouched = false 113 | } else if gestureRecognizer.state == UIGestureRecognizer.State.began { 114 | self.isTouched = true 115 | } 116 | 117 | if gestureRecognizer.state != UIGestureRecognizer.State.began 118 | && gestureRecognizer.state != UIGestureRecognizer.State.changed { 119 | return 120 | } 121 | 122 | let translation = gestureRecognizer.translation(in: self) 123 | gestureRecognizer.setTranslation(CGPoint.zero, in: self) 124 | 125 | self.ef_setValueWithTranslation(translation: translation.x) 126 | } 127 | 128 | func ef_updateTrackLayer() { 129 | let height: CGFloat = EFSliderViewHeight 130 | let width: CGFloat = self.bounds.width 131 | 132 | CATransaction.begin() 133 | CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) 134 | trackLayer.bounds = CGRect(x: 0, y: 0, width: width, height: EFSliderViewTrackHeight) 135 | trackLayer.position = CGPoint(x: self.bounds.width / 2, y: height / 2) 136 | CATransaction.commit() 137 | } 138 | // MARK: - Private methods 139 | private func ef_setValueWithTranslation(translation: CGFloat) { 140 | let width: CGFloat = self.bounds.width - thumbView.bounds.width 141 | let valueRange: CGFloat = maximumValue - minimumValue 142 | let value: CGFloat = self.value + valueRange * translation / width 143 | 144 | self.setValue(value: value) 145 | self.sendActions(for: UIControl.Event.valueChanged) 146 | } 147 | 148 | private func ef_updateLocations() { 149 | let size: Int = trackLayer.colors?.count ?? 2 150 | if size == trackLayer.locations?.count { 151 | return 152 | } 153 | 154 | let step = 1.0 / CGFloat(size - 1) 155 | var locations: [NSNumber] = [0] 156 | 157 | var i: Int = 1 158 | while i < size - 1 { 159 | locations.append(NSNumber(value: Double(CGFloat(i) * step))) 160 | i += 1 161 | } 162 | 163 | locations.append(1.0) 164 | trackLayer.locations = locations 165 | } 166 | 167 | private func ef_updateThumbPositionWithValue(value: CGFloat) { 168 | let thumbWidth: CGFloat = thumbView.bounds.width 169 | let thumbHeight: CGFloat = thumbView.bounds.height 170 | let width: CGFloat = self.bounds.width - thumbWidth 171 | 172 | if width == 0 { 173 | return 174 | } 175 | 176 | let percentage: CGFloat = (value - minimumValue) / (maximumValue - minimumValue) 177 | let position: CGFloat = width * percentage 178 | thumbView.frame = CGRect(x: position, y: 0, width: thumbWidth, height: thumbHeight) 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /EFColorPicker/Classes/EFThumbView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EFThumbView.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | public class EFThumbView: EFControl { 30 | 31 | private(set) var gestureRecognizer = UIPanGestureRecognizer(target: nil, action: nil) 32 | 33 | private let EFSliderViewThumbDimension: CGFloat = 28.0 34 | private lazy var thumbLayer: CALayer = { 35 | let layer = CALayer() 36 | layer.borderColor = UIColor.lightGray.withAlphaComponent(0.4).cgColor 37 | layer.borderWidth = 0.5 38 | layer.cornerRadius = EFSliderViewThumbDimension / 2 39 | layer.backgroundColor = UIColor.white.cgColor 40 | layer.shadowColor = UIColor.black.cgColor 41 | layer.shadowOffset = CGSize(width: 0, height: 3) 42 | layer.shadowRadius = 2 43 | layer.shadowOpacity = 0.3 44 | return layer 45 | }() 46 | 47 | override init(frame: CGRect) { 48 | super.init(frame: CGRect(x: frame.origin.x, y: frame.origin.y, 49 | width: EFSliderViewThumbDimension, height: EFSliderViewThumbDimension 50 | ) 51 | ) 52 | layer.addSublayer(thumbLayer) 53 | addGestureRecognizer(gestureRecognizer) 54 | } 55 | 56 | required public init?(coder aDecoder: NSCoder) { 57 | fatalError("init(coder:) has not been implemented") 58 | } 59 | 60 | override public func layoutSublayers(of layer: CALayer) { 61 | if layer != self.layer { 62 | return 63 | } 64 | 65 | CATransaction.begin() 66 | CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) 67 | thumbLayer.bounds = CGRect( 68 | x: 0, y: 0, width: EFSliderViewThumbDimension, height: EFSliderViewThumbDimension 69 | ) 70 | thumbLayer.position = CGPoint(x: EFSliderViewThumbDimension / 2, y: EFSliderViewThumbDimension / 2) 71 | CATransaction.commit() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Example/EFColorPicker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 11 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 12 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 13 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 14 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 15 | B51C092BC469372831AA3531 /* Pods_EFColorPicker_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC1F8C2CEEC11F9A071D0BB6 /* Pods_EFColorPicker_Example.framework */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 12ED54D72250990400B84777 /* README_CN.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README_CN.md; path = ../README_CN.md; sourceTree = ""; }; 20 | 4E0693B61CB89F59B63E299E /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 21 | 607FACD01AFB9204008FA782 /* EFColorPicker_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EFColorPicker_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 27 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 28 | 66363CE54747C0CE52946CEE /* Pods-EFColorPicker_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EFColorPicker_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-EFColorPicker_Tests/Pods-EFColorPicker_Tests.release.xcconfig"; sourceTree = ""; }; 29 | A1767F18911529A8B2770D80 /* Pods-EFColorPicker_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EFColorPicker_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-EFColorPicker_Example/Pods-EFColorPicker_Example.release.xcconfig"; sourceTree = ""; }; 30 | B80ED36874B9B1871306EF13 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 31 | C732E9AA81435E364115454A /* EFColorPicker.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = EFColorPicker.podspec; path = ../EFColorPicker.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 32 | C7AEF8D49DD9B32EE9F74210 /* Pods-EFColorPicker_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EFColorPicker_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-EFColorPicker_Tests/Pods-EFColorPicker_Tests.debug.xcconfig"; sourceTree = ""; }; 33 | CBA3BDDD7D56ED74B0A8FD74 /* Pods-EFColorPicker_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EFColorPicker_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-EFColorPicker_Example/Pods-EFColorPicker_Example.debug.xcconfig"; sourceTree = ""; }; 34 | D33A37D9FF5CC2221263E969 /* Pods_EFColorPicker_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_EFColorPicker_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | FC1F8C2CEEC11F9A071D0BB6 /* Pods_EFColorPicker_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_EFColorPicker_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | /* End PBXFileReference section */ 37 | 38 | /* Begin PBXFrameworksBuildPhase section */ 39 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 40 | isa = PBXFrameworksBuildPhase; 41 | buildActionMask = 2147483647; 42 | files = ( 43 | B51C092BC469372831AA3531 /* Pods_EFColorPicker_Example.framework in Frameworks */, 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 37A7F39F16F92CB73A28B206 /* Frameworks */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | FC1F8C2CEEC11F9A071D0BB6 /* Pods_EFColorPicker_Example.framework */, 54 | D33A37D9FF5CC2221263E969 /* Pods_EFColorPicker_Tests.framework */, 55 | ); 56 | name = Frameworks; 57 | sourceTree = ""; 58 | }; 59 | 3B20BF72B132F6E76C2728D5 /* Pods */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | CBA3BDDD7D56ED74B0A8FD74 /* Pods-EFColorPicker_Example.debug.xcconfig */, 63 | A1767F18911529A8B2770D80 /* Pods-EFColorPicker_Example.release.xcconfig */, 64 | C7AEF8D49DD9B32EE9F74210 /* Pods-EFColorPicker_Tests.debug.xcconfig */, 65 | 66363CE54747C0CE52946CEE /* Pods-EFColorPicker_Tests.release.xcconfig */, 66 | ); 67 | name = Pods; 68 | sourceTree = ""; 69 | }; 70 | 607FACC71AFB9204008FA782 = { 71 | isa = PBXGroup; 72 | children = ( 73 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 74 | 607FACD21AFB9204008FA782 /* Example for EFColorPicker */, 75 | 607FACD11AFB9204008FA782 /* Products */, 76 | 3B20BF72B132F6E76C2728D5 /* Pods */, 77 | 37A7F39F16F92CB73A28B206 /* Frameworks */, 78 | ); 79 | sourceTree = ""; 80 | }; 81 | 607FACD11AFB9204008FA782 /* Products */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 607FACD01AFB9204008FA782 /* EFColorPicker_Example.app */, 85 | ); 86 | name = Products; 87 | sourceTree = ""; 88 | }; 89 | 607FACD21AFB9204008FA782 /* Example for EFColorPicker */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 93 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 94 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 95 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 96 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 97 | 607FACD31AFB9204008FA782 /* Supporting Files */, 98 | ); 99 | name = "Example for EFColorPicker"; 100 | path = EFColorPicker; 101 | sourceTree = ""; 102 | }; 103 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 607FACD41AFB9204008FA782 /* Info.plist */, 107 | ); 108 | name = "Supporting Files"; 109 | sourceTree = ""; 110 | }; 111 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | C732E9AA81435E364115454A /* EFColorPicker.podspec */, 115 | 12ED54D72250990400B84777 /* README_CN.md */, 116 | 4E0693B61CB89F59B63E299E /* README.md */, 117 | B80ED36874B9B1871306EF13 /* LICENSE */, 118 | ); 119 | name = "Podspec Metadata"; 120 | sourceTree = ""; 121 | }; 122 | /* End PBXGroup section */ 123 | 124 | /* Begin PBXNativeTarget section */ 125 | 607FACCF1AFB9204008FA782 /* EFColorPicker_Example */ = { 126 | isa = PBXNativeTarget; 127 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "EFColorPicker_Example" */; 128 | buildPhases = ( 129 | 22961CC1D1B1D7A58067A08C /* [CP] Check Pods Manifest.lock */, 130 | 607FACCC1AFB9204008FA782 /* Sources */, 131 | 607FACCD1AFB9204008FA782 /* Frameworks */, 132 | 607FACCE1AFB9204008FA782 /* Resources */, 133 | 6AB6E6DE27DE9C0E2D1B3602 /* [CP] Embed Pods Frameworks */, 134 | ); 135 | buildRules = ( 136 | ); 137 | dependencies = ( 138 | ); 139 | name = EFColorPicker_Example; 140 | productName = EFColorPicker; 141 | productReference = 607FACD01AFB9204008FA782 /* EFColorPicker_Example.app */; 142 | productType = "com.apple.product-type.application"; 143 | }; 144 | /* End PBXNativeTarget section */ 145 | 146 | /* Begin PBXProject section */ 147 | 607FACC81AFB9204008FA782 /* Project object */ = { 148 | isa = PBXProject; 149 | attributes = { 150 | LastSwiftUpdateCheck = 0720; 151 | LastUpgradeCheck = 1020; 152 | ORGANIZATIONNAME = CocoaPods; 153 | TargetAttributes = { 154 | 607FACCF1AFB9204008FA782 = { 155 | CreatedOnToolsVersion = 6.3.1; 156 | LastSwiftMigration = 1020; 157 | }; 158 | }; 159 | }; 160 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "EFColorPicker" */; 161 | compatibilityVersion = "Xcode 3.2"; 162 | developmentRegion = en; 163 | hasScannedForEncodings = 0; 164 | knownRegions = ( 165 | en, 166 | Base, 167 | ); 168 | mainGroup = 607FACC71AFB9204008FA782; 169 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 170 | projectDirPath = ""; 171 | projectRoot = ""; 172 | targets = ( 173 | 607FACCF1AFB9204008FA782 /* EFColorPicker_Example */, 174 | ); 175 | }; 176 | /* End PBXProject section */ 177 | 178 | /* Begin PBXResourcesBuildPhase section */ 179 | 607FACCE1AFB9204008FA782 /* Resources */ = { 180 | isa = PBXResourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 184 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 185 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXResourcesBuildPhase section */ 190 | 191 | /* Begin PBXShellScriptBuildPhase section */ 192 | 22961CC1D1B1D7A58067A08C /* [CP] Check Pods Manifest.lock */ = { 193 | isa = PBXShellScriptBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | ); 197 | inputPaths = ( 198 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 199 | "${PODS_ROOT}/Manifest.lock", 200 | ); 201 | name = "[CP] Check Pods Manifest.lock"; 202 | outputPaths = ( 203 | "$(DERIVED_FILE_DIR)/Pods-EFColorPicker_Example-checkManifestLockResult.txt", 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | shellPath = /bin/sh; 207 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 208 | showEnvVarsInLog = 0; 209 | }; 210 | 6AB6E6DE27DE9C0E2D1B3602 /* [CP] Embed Pods Frameworks */ = { 211 | isa = PBXShellScriptBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | ); 215 | inputPaths = ( 216 | "${PODS_ROOT}/Target Support Files/Pods-EFColorPicker_Example/Pods-EFColorPicker_Example-frameworks.sh", 217 | "${BUILT_PRODUCTS_DIR}/EFColorPicker/EFColorPicker.framework", 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EFColorPicker.framework", 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | shellPath = /bin/sh; 225 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-EFColorPicker_Example/Pods-EFColorPicker_Example-frameworks.sh\"\n"; 226 | showEnvVarsInLog = 0; 227 | }; 228 | /* End PBXShellScriptBuildPhase section */ 229 | 230 | /* Begin PBXSourcesBuildPhase section */ 231 | 607FACCC1AFB9204008FA782 /* Sources */ = { 232 | isa = PBXSourcesBuildPhase; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 236 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXSourcesBuildPhase section */ 241 | 242 | /* Begin PBXVariantGroup section */ 243 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 244 | isa = PBXVariantGroup; 245 | children = ( 246 | 607FACDA1AFB9204008FA782 /* Base */, 247 | ); 248 | name = Main.storyboard; 249 | sourceTree = ""; 250 | }; 251 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 252 | isa = PBXVariantGroup; 253 | children = ( 254 | 607FACDF1AFB9204008FA782 /* Base */, 255 | ); 256 | name = LaunchScreen.xib; 257 | sourceTree = ""; 258 | }; 259 | /* End PBXVariantGroup section */ 260 | 261 | /* Begin XCBuildConfiguration section */ 262 | 607FACED1AFB9204008FA782 /* Debug */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | ALWAYS_SEARCH_USER_PATHS = NO; 266 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 267 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 268 | CLANG_CXX_LIBRARY = "libc++"; 269 | CLANG_ENABLE_MODULES = YES; 270 | CLANG_ENABLE_OBJC_ARC = YES; 271 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 272 | CLANG_WARN_BOOL_CONVERSION = YES; 273 | CLANG_WARN_COMMA = YES; 274 | CLANG_WARN_CONSTANT_CONVERSION = YES; 275 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 277 | CLANG_WARN_EMPTY_BODY = YES; 278 | CLANG_WARN_ENUM_CONVERSION = YES; 279 | CLANG_WARN_INFINITE_RECURSION = YES; 280 | CLANG_WARN_INT_CONVERSION = YES; 281 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 282 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 283 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 285 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 286 | CLANG_WARN_STRICT_PROTOTYPES = YES; 287 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 288 | CLANG_WARN_UNREACHABLE_CODE = YES; 289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 290 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 291 | COPY_PHASE_STRIP = NO; 292 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | ENABLE_TESTABILITY = YES; 295 | GCC_C_LANGUAGE_STANDARD = gnu99; 296 | GCC_DYNAMIC_NO_PIC = NO; 297 | GCC_NO_COMMON_BLOCKS = YES; 298 | GCC_OPTIMIZATION_LEVEL = 0; 299 | GCC_PREPROCESSOR_DEFINITIONS = ( 300 | "DEBUG=1", 301 | "$(inherited)", 302 | ); 303 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 306 | GCC_WARN_UNDECLARED_SELECTOR = YES; 307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 308 | GCC_WARN_UNUSED_FUNCTION = YES; 309 | GCC_WARN_UNUSED_VARIABLE = YES; 310 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 311 | MTL_ENABLE_DEBUG_INFO = YES; 312 | ONLY_ACTIVE_ARCH = YES; 313 | SDKROOT = iphoneos; 314 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 315 | SWIFT_VERSION = 5.0; 316 | }; 317 | name = Debug; 318 | }; 319 | 607FACEE1AFB9204008FA782 /* Release */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_COMMA = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INFINITE_RECURSION = YES; 337 | CLANG_WARN_INT_CONVERSION = YES; 338 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 340 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 341 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 342 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 343 | CLANG_WARN_STRICT_PROTOTYPES = YES; 344 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 348 | COPY_PHASE_STRIP = NO; 349 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 350 | ENABLE_NS_ASSERTIONS = NO; 351 | ENABLE_STRICT_OBJC_MSGSEND = YES; 352 | GCC_C_LANGUAGE_STANDARD = gnu99; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 361 | MTL_ENABLE_DEBUG_INFO = NO; 362 | SDKROOT = iphoneos; 363 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 364 | SWIFT_VERSION = 5.0; 365 | VALIDATE_PRODUCT = YES; 366 | }; 367 | name = Release; 368 | }; 369 | 607FACF01AFB9204008FA782 /* Debug */ = { 370 | isa = XCBuildConfiguration; 371 | baseConfigurationReference = CBA3BDDD7D56ED74B0A8FD74 /* Pods-EFColorPicker_Example.debug.xcconfig */; 372 | buildSettings = { 373 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 374 | DEVELOPMENT_TEAM = ""; 375 | INFOPLIST_FILE = EFColorPicker/Info.plist; 376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 377 | MARKETING_VERSION = 5.2.0; 378 | MODULE_NAME = ExampleApp; 379 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_VERSION = 5.0; 382 | TARGETED_DEVICE_FAMILY = "1,2"; 383 | }; 384 | name = Debug; 385 | }; 386 | 607FACF11AFB9204008FA782 /* Release */ = { 387 | isa = XCBuildConfiguration; 388 | baseConfigurationReference = A1767F18911529A8B2770D80 /* Pods-EFColorPicker_Example.release.xcconfig */; 389 | buildSettings = { 390 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 391 | DEVELOPMENT_TEAM = ""; 392 | INFOPLIST_FILE = EFColorPicker/Info.plist; 393 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 394 | MARKETING_VERSION = 5.2.0; 395 | MODULE_NAME = ExampleApp; 396 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 397 | PRODUCT_NAME = "$(TARGET_NAME)"; 398 | SWIFT_VERSION = 5.0; 399 | TARGETED_DEVICE_FAMILY = "1,2"; 400 | }; 401 | name = Release; 402 | }; 403 | /* End XCBuildConfiguration section */ 404 | 405 | /* Begin XCConfigurationList section */ 406 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "EFColorPicker" */ = { 407 | isa = XCConfigurationList; 408 | buildConfigurations = ( 409 | 607FACED1AFB9204008FA782 /* Debug */, 410 | 607FACEE1AFB9204008FA782 /* Release */, 411 | ); 412 | defaultConfigurationIsVisible = 0; 413 | defaultConfigurationName = Release; 414 | }; 415 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "EFColorPicker_Example" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 607FACF01AFB9204008FA782 /* Debug */, 419 | 607FACF11AFB9204008FA782 /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | /* End XCConfigurationList section */ 425 | }; 426 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 427 | } 428 | -------------------------------------------------------------------------------- /Example/EFColorPicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/EFColorPicker.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/EFColorPicker.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/EFColorPicker/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | @UIApplicationMain 30 | class AppDelegate: UIResponder, UIApplicationDelegate { 31 | 32 | var window: UIWindow? 33 | 34 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 35 | 36 | UIApplication.shared.isStatusBarHidden = false 37 | return true 38 | } 39 | 40 | func applicationWillResignActive(_ application: UIApplication) { 41 | 42 | } 43 | 44 | func applicationDidEnterBackground(_ application: UIApplication) { 45 | 46 | } 47 | 48 | func applicationWillEnterForeground(_ application: UIApplication) { 49 | 50 | } 51 | 52 | func applicationDidBecomeActive(_ application: UIApplication) { 53 | 54 | } 55 | 56 | func applicationWillTerminate(_ application: UIApplication) { 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Example/EFColorPicker/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/EFColorPicker/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 71 | 83 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20@2x-1.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20@2x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20@2x-2.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20@2x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/20@3x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/29@2x-1.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/29@2x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/29@3x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/40@2x-1.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/40@2x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/40@3x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/60@2x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/60@3x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/76@2x.png -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "29@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "20.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "20@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "29.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "29@2x-1.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "20@2x-2.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "40@2x-1.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "167.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "1024.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/welcome.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "welcome.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/EFColorPicker/Images.xcassets/welcome.imageset/welcome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFPrefix/EFColorPicker/10e0b7de7179d3b94d05be0a3a5a771052752247/Example/EFColorPicker/Images.xcassets/welcome.imageset/welcome.jpg -------------------------------------------------------------------------------- /Example/EFColorPicker/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarHidden 34 | 35 | UIStatusBarStyle 36 | UIStatusBarStyleDefault 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | 41 | UIViewControllerBasedStatusBarAppearance 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Example/EFColorPicker/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // EFColorPicker 4 | // 5 | // Created by EyreFree on 2017/9/28. 6 | // 7 | // Copyright (c) 2017 EyreFree 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import UIKit 28 | import EFColorPicker 29 | 30 | class ViewController: UIViewController, UIPopoverPresentationControllerDelegate, EFColorSelectionViewControllerDelegate { 31 | 32 | @IBOutlet weak var testButton: UIButton! 33 | var isColorTextFieldHidden: Bool = true 34 | 35 | override func viewDidLoad() { 36 | super.viewDidLoad() 37 | 38 | self.navigationItem.title = "EFColorPicker" 39 | self.view.backgroundColor = UIColor.white 40 | 41 | testButton.layer.borderColor = UIColor.black.cgColor 42 | testButton.layer.cornerRadius = 4.5 43 | testButton.layer.borderWidth = 0.5 44 | testButton.setTitleColor(UIColor.black, for: .normal) 45 | testButton.setTitle("isColorTextFieldHidden: \(isColorTextFieldHidden)", for: .normal) 46 | testButton.addTarget(self, action: #selector(onTestButtonClick(_:)), for: .touchUpInside) 47 | } 48 | 49 | // 50 | @objc func onTestButtonClick(_ sender: UIButton) { 51 | isColorTextFieldHidden = !isColorTextFieldHidden 52 | testButton.setTitle("isColorTextFieldHidden: \(isColorTextFieldHidden)", for: .normal) 53 | } 54 | 55 | // Programmatically 56 | @IBAction func onButtonClick(_ sender: UIButton) { 57 | let colorSelectionController = EFColorSelectionViewController() 58 | 59 | let navCtrl = UINavigationController(rootViewController: colorSelectionController) 60 | navCtrl.navigationBar.backgroundColor = UIColor.white 61 | navCtrl.navigationBar.isTranslucent = false 62 | navCtrl.modalPresentationStyle = UIModalPresentationStyle.popover 63 | navCtrl.popoverPresentationController?.delegate = self 64 | navCtrl.popoverPresentationController?.sourceView = sender 65 | navCtrl.popoverPresentationController?.sourceRect = sender.bounds 66 | navCtrl.preferredContentSize = colorSelectionController.view.systemLayoutSizeFitting( 67 | UIView.layoutFittingCompressedSize 68 | ) 69 | 70 | colorSelectionController.isColorTextFieldHidden = isColorTextFieldHidden 71 | colorSelectionController.delegate = self 72 | colorSelectionController.color = self.view.backgroundColor ?? UIColor.white 73 | // colorSelectionController.setMode(mode: EFColorSelectionMode.hsb) 74 | 75 | if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass { 76 | let doneBtn: UIBarButtonItem = UIBarButtonItem( 77 | title: NSLocalizedString("Done", comment: ""), 78 | style: UIBarButtonItem.Style.done, 79 | target: self, 80 | action: #selector(ef_dismissViewController(sender:)) 81 | ) 82 | colorSelectionController.navigationItem.rightBarButtonItem = doneBtn 83 | } 84 | self.present(navCtrl, animated: true, completion: nil) 85 | } 86 | 87 | // Storyboard 88 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 89 | if "showPopover" == segue.identifier { 90 | guard let destNav: UINavigationController = segue.destination as? UINavigationController else { 91 | return 92 | } 93 | if let size = destNav.visibleViewController?.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) { 94 | destNav.preferredContentSize = size 95 | } 96 | destNav.popoverPresentationController?.delegate = self 97 | if let colorSelectionController = destNav.visibleViewController as? EFColorSelectionViewController { 98 | colorSelectionController.isColorTextFieldHidden = isColorTextFieldHidden 99 | colorSelectionController.delegate = self 100 | colorSelectionController.color = self.view.backgroundColor ?? UIColor.white 101 | 102 | if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass { 103 | let doneBtn: UIBarButtonItem = UIBarButtonItem( 104 | title: NSLocalizedString("Done", comment: ""), 105 | style: UIBarButtonItem.Style.done, 106 | target: self, 107 | action: #selector(ef_dismissViewController(sender:)) 108 | ) 109 | colorSelectionController.navigationItem.rightBarButtonItem = doneBtn 110 | } 111 | } 112 | } 113 | } 114 | 115 | // MARK:- EFColorSelectionViewControllerDelegate 116 | func colorViewController(_ colorViewCntroller: EFColorSelectionViewController, didChangeColor color: UIColor) { 117 | self.view.backgroundColor = color 118 | 119 | // TODO: You can do something here when color changed. 120 | print("New color: " + color.debugDescription) 121 | } 122 | 123 | // MARK:- Private 124 | @objc func ef_dismissViewController(sender: UIBarButtonItem) { 125 | self.dismiss(animated: true) { 126 | [weak self] in 127 | if let _ = self { 128 | // TODO: You can do something here when EFColorPicker close. 129 | print("EFColorPicker closed.") 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | use_frameworks! 4 | 5 | target 'EFColorPicker_Example' do 6 | pod 'EFColorPicker', :path => '../' 7 | 8 | end 9 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - EFColorPicker (5.2.2) 3 | 4 | DEPENDENCIES: 5 | - EFColorPicker (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | EFColorPicker: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | EFColorPicker: 6a67701ea834bd9b6f4c3e29aa2e78a6d4ea88f4 13 | 14 | PODFILE CHECKSUM: de9ac90577be3d3983cbb217909bf8ce7e9d48c0 15 | 16 | COCOAPODS: 1.10.1 17 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Check List 2 | 3 | Thanks for considering to open an issue. Before you submit your issue, please confirm these boxes are checked. 4 | 5 | - [ ] I have read the [README.md](https://github.com/EFPrefix/EFColorPicker/blob/master/README.md), but there is no information I need. 6 | - [ ] I have searched in [existing issues](https://github.com/EFPrefix/EFColorPicker/issues?utf8=%E2%9C%93&q=is%3Aissue), but did find a same one. 7 | 8 | ### Issue Description 9 | 10 | #### Description 11 | 12 | [Tell us about the issue] 13 | 14 | #### Reproduce 15 | 16 | [The steps to reproduce this issue. What are the parameters, where did you put your code, etc.] 17 | 18 | #### Other Comment 19 | 20 | [Add anything else here] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 EyreFree 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "EFColorPicker", 6 | platforms: [ 7 | .iOS(.v8), 8 | ], 9 | products: [ 10 | .library(name: "EFColorPicker", targets: ["EFColorPicker"]), 11 | ], 12 | targets: [ 13 | .target(name: "EFColorPicker", path: "EFColorPicker"), 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DEPRECATED, use [UIColorPickerViewController](https://developer.apple.com/documentation/uikit/uicolorpickerviewcontroller) / [ColorPicker](https://developer.apple.com/documentation/swiftui/colorpicker) instead. 2 | 3 | --- 4 | 5 | ![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/EFColorPicker.png) 6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |

37 | 38 | EFColorPicker is a lightweight color picker in Swift, inspired by [MSColorPicker](https://github.com/sgl0v/MSColorPicker). 39 | 40 | > [中文介绍](https://github.com/EFPrefix/EFColorPicker/blob/master/README_CN.md) 41 | 42 | ## Overview 43 | 44 | Color picker component for iOS. It allows the user to select a color with color components. Key features: 45 | 46 | - iPhone & iPad support 47 | - Adaptive User Interface 48 | - Supports RGB and HSB color models 49 | - Well-documented 50 | - Compatible with iOS 8.0 (iPhone & iPad) and higher 51 | 52 | ## Preview 53 | 54 | | iPhone | | iPad | 55 | |:---------------------:|:---------------------:|:---------------------:| 56 | ![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone.png)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone.gif)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_ipad.gif) 57 | 58 | ## Example 59 | 60 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 61 | 62 | Then build and run `EFColorPicker.xcworkspace` in Xcode, the demo shows how to use and integrate the EFColorPicker into your project. 63 | 64 | ## Requirements 65 | 66 | | Version | Needs | 67 | |:-------|:-----------------------------------------------| 68 | | <5.0 | Xcode 10.0+
Swift 4.2+
iOS 8.0+ | 69 | | 5.x | Xcode 10.2+
Swift 5.0+
iOS 8.0+ | 70 | 71 | ## Installation 72 | 73 | ### CocoaPods 74 | 75 | EFColorPicker is available through [CocoaPods](http://cocoapods.org). To install 76 | it, simply add the following line to your Podfile: 77 | 78 | ```ruby 79 | pod 'EFColorPicker' 80 | ``` 81 | ### Carthage 82 | 83 | You can use [Carthage](https://github.com/Carthage/Carthage) to install `EFColorPicker` by adding that to your Cartfile: 84 | 85 | ``` Swift 86 | github "EFPrefix/EFColorPicker" 87 | ``` 88 | 89 | ### Swift Package Manager 90 | 91 | The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the Swift compiler. 92 | 93 | Once you have your Swift package set up, adding EFColorPicker as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`. 94 | 95 | ```swift 96 | dependencies: [ 97 | .package(url: "https://github.com/EFPrefix/EFColorPicker.git", .upToNextMinor(from: "5.2.2")) 98 | ] 99 | ``` 100 | 101 | ## Use 102 | 103 | 1. First, include EFColorPicker in your project: 104 | 105 | ```swift 106 | import EFColorPicker 107 | ``` 108 | 109 | 2. Next, we can call EFColorPicker with pure code: 110 | 111 | ```swift 112 | let colorSelectionController = EFColorSelectionViewController() 113 | let navCtrl = UINavigationController(rootViewController: colorSelectionController) 114 | navCtrl.navigationBar.backgroundColor = UIColor.white 115 | navCtrl.navigationBar.isTranslucent = false 116 | navCtrl.modalPresentationStyle = UIModalPresentationStyle.popover 117 | navCtrl.popoverPresentationController?.delegate = self 118 | navCtrl.popoverPresentationController?.sourceView = sender 119 | navCtrl.popoverPresentationController?.sourceRect = sender.bounds 120 | navCtrl.preferredContentSize = colorSelectionController.view.systemLayoutSizeFitting( 121 | UILayoutFittingCompressedSize 122 | ) 123 | 124 | colorSelectionController.delegate = self 125 | colorSelectionController.color = self.view.backgroundColor ?? UIColor.white 126 | colorSelectionController.setMode(mode: EFColorSelectionMode.all) 127 | 128 | if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass { 129 | let doneBtn: UIBarButtonItem = UIBarButtonItem( 130 | title: NSLocalizedString("Done", comment: ""), 131 | style: UIBarButtonItemStyle.done, 132 | target: self, 133 | action: #selector(ef_dismissViewController(sender:)) 134 | ) 135 | colorSelectionController.navigationItem.rightBarButtonItem = doneBtn 136 | } 137 | self.present(navCtrl, animated: true, completion: nil) 138 | ``` 139 | 140 | Also we can use EFColorPicker in Storyboard: 141 | 142 | ```swift 143 | if "showPopover" == segue.identifier { 144 | guard let destNav: UINavigationController = segue.destination as? UINavigationController else { 145 | return 146 | } 147 | if let size = destNav.visibleViewController?.view.systemLayoutSizeFitting(UILayoutFittingCompressedSize) { 148 | destNav.preferredContentSize = size 149 | } 150 | destNav.popoverPresentationController?.delegate = self 151 | if let colorSelectionController = destNav.visibleViewController as? EFColorSelectionViewController { 152 | colorSelectionController.delegate = self 153 | colorSelectionController.color = self.view.backgroundColor ?? UIColor.white 154 | 155 | if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass { 156 | let doneBtn: UIBarButtonItem = UIBarButtonItem( 157 | title: NSLocalizedString("Done", comment: ""), 158 | style: UIBarButtonItemStyle.done, 159 | target: self, 160 | action: #selector(ef_dismissViewController(sender:)) 161 | ) 162 | colorSelectionController.navigationItem.rightBarButtonItem = doneBtn 163 | } 164 | } 165 | } 166 | ``` 167 | 168 | You can control the visibility of color textField by change the `isColorTextFieldHidden` property of `EFColorSelectionViewController`, for example: 169 | 170 | | isColorTextFieldHidden: true | | isColorTextFieldHidden: false | | 171 | |:---------------------:|:---------------------:|:---------------------:|:---------------------:| 172 | ![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone1.png)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone2.png)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone3.png)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone4.png) 173 | 174 | For more detail, please see the demo. 175 | 176 | 3. Last but not the least, you should implement `EFColorSelectionViewControllerDelegate` so you can sense the color changes: 177 | 178 | ```swift 179 | // MARK:- EFColorSelectionViewControllerDelegate 180 | func colorViewController(colorViewCntroller: EFColorSelectionViewController, didChangeColor color: UIColor) { 181 | self.view.backgroundColor = color 182 | 183 | // TODO: You can do something here when color changed. 184 | print("New color: " + color.debugDescription) 185 | } 186 | ``` 187 | 188 | ## Apps using EFColorPicker 189 | 190 | 191 | 192 | 197 | 202 | 207 | 208 |
193 | 194 | 195 | 196 | 198 | 199 | 200 | 201 | 203 | 204 | 205 | 206 |
209 | 210 | ## Author 211 | 212 | EyreFree, eyrefree@eyrefree.org 213 | 214 | ## License 215 | 216 | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/License_icon-mit-88x31-2.svg/128px-License_icon-mit-88x31-2.svg.png) 217 | 218 | EFColorPicker is available under the MIT license. See the LICENSE file for more info. 219 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | 已归档,建议使用 [UIColorPickerViewController](https://developer.apple.com/documentation/uikit/uicolorpickerviewcontroller) / [ColorPicker](https://developer.apple.com/documentation/swiftui/colorpicker) 替代。 2 | 3 | --- 4 | 5 | ![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/EFColorPicker.png) 6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |

40 | 41 | EFColorPicker 是一个纯 Swift 的轻量级 iOS 颜色选择器,受 [MSColorPicker](https://github.com/sgl0v/MSColorPicker) 启发。 42 | 43 | > [English Introduction](https://github.com/EFPrefix/EFColorPicker/blob/master/README.md) 44 | 45 | ## 概述 46 | 47 | iOS 颜色选择器组件,它能够让用户选择自定义颜色,关键特性如下: 48 | 49 | - 支持 iPhone 和 iPad 50 | - 自适应的用户界面 51 | - 支持 RGB 和 HSB 两种颜色模式 52 | - 比较完善的文档和注释 53 | - 支持 iOS 8.0 (iPhone & iPad) 及更高版本 54 | 55 | ## 预览 56 | 57 | | iPhone | | iPad | 58 | |:---------------------:|:---------------------:|:---------------------:| 59 | ![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone.png)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone.gif)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_ipad.gif) 60 | 61 | ## 示例 62 | 63 | 1. 利用 `git clone` 命令下载本仓库; 64 | 2. 利用 cd 命令切换到 Example 目录下,执行 `pod install` 命令; 65 | 3. 随后打开 `EFColorPicker.xcworkspace` 编译即可。 66 | 67 | 或执行以下命令: 68 | 69 | ```bash 70 | git clone git@github.com:EFPrefix/EFColorPicker.git; cd EFColorPicker/Example; pod install; open EFColorPicker.xcworkspace 71 | ``` 72 | 73 | ## 环境 74 | 75 | | 版本 | 需求 | 76 | |:-------|:-----------------------------------------------| 77 | | <5.0 | Xcode 10.0+
Swift 4.2+
iOS 8.0+ | 78 | | 5.x | Xcode 10.2+
Swift 5.0+
iOS 8.0+ | 79 | 80 | ## 安装 81 | 82 | ### CocoaPods 83 | 84 | EFColorPicker 可以通过 [CocoaPods](http://cocoapods.org) 进行获取。只需要在你的 Podfile 中添加如下代码就能实现引入: 85 | 86 | ``` 87 | pod "EFColorPicker" 88 | ``` 89 | 90 | ### Swift Package Manager 91 | 92 | [Swift Package Manager](https://swift.org/package-manager/) 是一个集成在 Swift 编译器中的用来进行 Swift 代码自动化发布的工具。 93 | 94 | 如果你已经建立了你的 Swift 包,将 EFColorPicker 加入依赖是十分容易的,只需要将其添加到你的 `Package.swift` 文件的 `dependencies` 项中即可: 95 | 96 | ```swift 97 | dependencies: [ 98 | .package(url: "https://github.com/EFPrefix/EFColorPicker.git", .upToNextMinor(from: "5.2.2")) 99 | ] 100 | ``` 101 | 102 | ## 使用 103 | 104 | 1. 首先,需要导入 EFColorPicker 库: 105 | 106 | ```swift 107 | import EFColorPicker 108 | ``` 109 | 110 | 2. 接下来,可以通过纯代码调用: 111 | 112 | ```swift 113 | let colorSelectionController = EFColorSelectionViewController() 114 | let navCtrl = UINavigationController(rootViewController: colorSelectionController) 115 | navCtrl.navigationBar.backgroundColor = UIColor.white 116 | navCtrl.navigationBar.isTranslucent = false 117 | navCtrl.modalPresentationStyle = UIModalPresentationStyle.popover 118 | navCtrl.popoverPresentationController?.delegate = self 119 | navCtrl.popoverPresentationController?.sourceView = sender 120 | navCtrl.popoverPresentationController?.sourceRect = sender.bounds 121 | navCtrl.preferredContentSize = colorSelectionController.view.systemLayoutSizeFitting( 122 | UILayoutFittingCompressedSize 123 | ) 124 | 125 | colorSelectionController.delegate = self 126 | colorSelectionController.color = self.view.backgroundColor ?? UIColor.white 127 | colorSelectionController.setMode(mode: EFColorSelectionMode.all) 128 | 129 | if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass { 130 | let doneBtn: UIBarButtonItem = UIBarButtonItem( 131 | title: NSLocalizedString("Done", comment: ""), 132 | style: UIBarButtonItemStyle.done, 133 | target: self, 134 | action: #selector(ef_dismissViewController(sender:)) 135 | ) 136 | colorSelectionController.navigationItem.rightBarButtonItem = doneBtn 137 | } 138 | self.present(navCtrl, animated: true, completion: nil) 139 | ``` 140 | 141 | 也可以通过 Storyboard 调用: 142 | 143 | ```swift 144 | if "showPopover" == segue.identifier { 145 | guard let destNav: UINavigationController = segue.destination as? UINavigationController else { 146 | return 147 | } 148 | if let size = destNav.visibleViewController?.view.systemLayoutSizeFitting(UILayoutFittingCompressedSize) { 149 | destNav.preferredContentSize = size 150 | } 151 | destNav.popoverPresentationController?.delegate = self 152 | if let colorSelectionController = destNav.visibleViewController as? EFColorSelectionViewController { 153 | colorSelectionController.delegate = self 154 | colorSelectionController.color = self.view.backgroundColor ?? UIColor.white 155 | 156 | if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass { 157 | let doneBtn: UIBarButtonItem = UIBarButtonItem( 158 | title: NSLocalizedString("Done", comment: ""), 159 | style: UIBarButtonItemStyle.done, 160 | target: self, 161 | action: #selector(ef_dismissViewController(sender:)) 162 | ) 163 | colorSelectionController.navigationItem.rightBarButtonItem = doneBtn 164 | } 165 | } 166 | } 167 | ``` 168 | 169 | 你可以通过修改 `EFColorSelectionViewController` 的 `isColorTextFieldHidden` 属性来控制颜色编辑框的可见性,效果如下: 170 | 171 | | isColorTextFieldHidden: true | | isColorTextFieldHidden: false | | 172 | |:---------------------:|:---------------------:|:---------------------:|:---------------------:| 173 | ![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone1.png)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone2.png)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone3.png)|![](https://raw.githubusercontent.com/EFPrefix/EFColorPicker/master/Assets/sample_iphone4.png) 174 | 175 | 具体可参考示例程序。 176 | 177 | 3. 最后,不要忘记调用的 ViewController 需要继承 EFColorSelectionViewControllerDelegate 来及时获取颜色的变化: 178 | 179 | ```swift 180 | // MARK:- EFColorSelectionViewControllerDelegate 181 | func colorViewController(colorViewCntroller: EFColorSelectionViewController, didChangeColor color: UIColor) { 182 | self.view.backgroundColor = color 183 | 184 | // TODO: You can do something here when color changed. 185 | print("New color: " + color.debugDescription) 186 | } 187 | ``` 188 | 189 | ## 使用 EFColorPicker 的应用 190 | 191 | 192 | 193 | 198 | 203 | 208 | 209 |
194 | 195 | 196 | 197 | 199 | 200 | 201 | 202 | 204 | 205 | 206 | 207 |
210 | 211 | ## 作者 212 | 213 | EyreFree, eyrefree@eyrefree.org 214 | 215 | ## 协议 216 | 217 | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/License_icon-mit-88x31-2.svg/128px-License_icon-mit-88x31-2.svg.png) 218 | 219 | EFColorPicker 基于 MIT 协议进行分发和使用,更多信息参见协议文件。 220 | -------------------------------------------------------------------------------- /Startup.sh: -------------------------------------------------------------------------------- 1 | if ! command -v pod > /dev/null; then 2 | printf 'CocoaPods is not installed.\n' 3 | printf 'See https://github.com/CocoaPods/CocoaPods for install instructions.\n' 4 | exit 1 5 | fi 6 | cd Example; 7 | pod repo update; 8 | pod install; 9 | cd ..; 10 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect --------------------------------------------------------------------------------