added files via upload
							
								
								
									
										16
									
								
								Tweaks/Alderis/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,16 @@ | ||||
| # Crap | ||||
| .DS_Store | ||||
|  | ||||
| # Xcode | ||||
| *.pbxuser | ||||
| !default.pbxuser | ||||
| xcuserdata | ||||
| *.xccheckout | ||||
| *.xcuserstate | ||||
|  | ||||
| # Carthage | ||||
| Carthage/ | ||||
|  | ||||
| # Theos | ||||
| .theos | ||||
| packages/ | ||||
							
								
								
									
										36
									
								
								Tweaks/Alderis/.jazzy.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,36 @@ | ||||
| clean: true | ||||
| author: HASHBANG Productions | ||||
| author_url: https://hbang.github.io/ | ||||
| github_url: https://github.com/hbang/Alderis | ||||
| root_url: https://hbang.github.io/Alderis/ | ||||
| dash_url: https://hbang.github.io/Alderis/docsets/Alderis.xml | ||||
| documentation: info/*.md | ||||
| hide_documentation_coverage: true | ||||
|  | ||||
| custom_categories: | ||||
|   - name: "Guides" | ||||
|     children: | ||||
|       - "Preference Bundles" | ||||
|       - "Migrating to 1.1" | ||||
|  | ||||
|   - name: "Color Picker" | ||||
|     children: | ||||
|       - ColorPickerViewController | ||||
|       - ColorPickerConfiguration | ||||
|       - ColorPickerDelegate | ||||
|       - ColorPickerTab | ||||
|    | ||||
|   - name: "UI Components" | ||||
|     children: | ||||
|       - ColorWell | ||||
|  | ||||
|   - name: "Extensions" | ||||
|     children: | ||||
|       - UIColor | ||||
|       - ColorPropertyListValue | ||||
|       - String | ||||
|       - Array | ||||
|  | ||||
|   - name: "Deprecated" | ||||
|     children: | ||||
|       - ColorPickerCircleView | ||||
							
								
								
									
										61
									
								
								Tweaks/Alderis/.swiftlint.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,61 @@ | ||||
| disabled_rules: | ||||
|   - trailing_comma | ||||
|   - nesting | ||||
|   - fallthrough | ||||
|   - shorthand_operator | ||||
|   - todo | ||||
|   - large_tuple | ||||
|   - identifier_name | ||||
|   - type_name | ||||
|   - type_body_length | ||||
|   # TODO: Why is vertical_parameter_alignment giving false positives? | ||||
|   - vertical_parameter_alignment | ||||
|   - vertical_parameter_alignment_on_call | ||||
|   # TODO: Enable when removing support for older Swift versions (<5.6) | ||||
|   - unavailable_condition | ||||
| opt_in_rules: | ||||
|   - closure_end_indentation | ||||
|   - closure_spacing | ||||
|   - contains_over_first_not_nil | ||||
|   - empty_count | ||||
|   - explicit_init | ||||
|   - fatal_error_message | ||||
|   - first_where | ||||
|   - joined_default_parameter | ||||
|   - literal_expression_end_indentation | ||||
|   - overridden_super_call | ||||
|   - prohibited_super_call | ||||
|   - sorted_first_last | ||||
|   - unneeded_parentheses_in_closure_argument | ||||
|   - vertical_parameter_alignment_on_call | ||||
|   - yoda_condition | ||||
|   - nslocalizedstring_key | ||||
|   - unused_setter_value | ||||
| custom_rules: | ||||
|   comment_whitespace: | ||||
|     name: "Comment Whitespace" | ||||
|     regex: //\S | ||||
|     match_kinds: comment | ||||
|     message: "Comments must begin with a whitespace character" | ||||
|   spaces_not_tabs: | ||||
|     name: "Tabs not Spaces" | ||||
|     regex: ^( ) | ||||
|     message: "Use tabs instead of spaces" | ||||
|   point_zero: | ||||
|     name: "Point Zero" | ||||
|     regex: '(?<!iOS\s)(?<!macOS\s)(?<!\.)\b[\d_]+\.0\b' | ||||
|     match_kinds: | ||||
|       - number | ||||
|       - attribute.builtin | ||||
|     message: "Don't add a .0 to the end of floating point literals" | ||||
|   color_init: | ||||
|     name: "Color Initializer" | ||||
|     regex: 'UIColor\(red: (.+), green: \1, blue: \1, alpha: .+\)' | ||||
|     message: "Use UIColor(white:alpha:)" | ||||
| force_try: warning | ||||
| force_cast: warning | ||||
| function_body_length: 200 | ||||
| line_length: 200 | ||||
| file_length: 450 | ||||
| cyclomatic_complexity: 12 | ||||
| function_parameter_count: 7 | ||||
							
								
								
									
										30
									
								
								Tweaks/Alderis/Alderis.podspec
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | ||||
| Pod::Spec.new do |spec| | ||||
|   spec.name         = "Alderis" | ||||
|   spec.version      = "1.2.0" | ||||
|   spec.summary      = "A fresh new color picker, with a gentle, fun, and dead simple user interface." | ||||
|   spec.description  = <<-DESC | ||||
|                       Alderis is a fresh new color picker, with a gentle, fun, and dead simple user | ||||
|                       interface. It aims to incorporate the usual elements of a color picker, in a way | ||||
|                       that users will find easy and fun to use. | ||||
|  | ||||
|                       The user can start by selecting a color they like on the initial color palette | ||||
|                       tab, and either accept it, or refine it using the color wheel and adjustment | ||||
|                       sliders found on the two other tabs. | ||||
|                       DESC | ||||
|   spec.homepage     = "https://github.com/hbang/Alderis" | ||||
|   spec.screenshots  = "https://github.com/hbang/Alderis/raw/main/screenshots/alderis-1.jpg", | ||||
|                       "https://github.com/hbang/Alderis/raw/main/screenshots/alderis-2.jpg", | ||||
|                       "https://github.com/hbang/Alderis/raw/main/screenshots/alderis-3.jpg", | ||||
|                       "https://github.com/hbang/Alderis/raw/main/screenshots/alderis-4.jpg" | ||||
|   spec.license      = "Apache License, Version 2.0" | ||||
|   spec.author       = "HASHBANG Productions" | ||||
|   spec.social_media_url = "https://twitter.com/hashbang" | ||||
|  | ||||
|   spec.swift_versions = "5.0" | ||||
|   spec.platform     = :ios, "12.0" | ||||
|  | ||||
|   spec.source       = { :git => "https://github.com/hbang/Alderis.git", :tag => "#{spec.version}" } | ||||
|   spec.requires_arc = true | ||||
|   spec.source_files = [ "Alderis/*.swift", "Alderis/*.h" ] | ||||
|   spec.resource_bundles = { "Alderis" => "Alderis/Assets-ios12.xcassets" } | ||||
| end | ||||
							
								
								
									
										482
									
								
								Tweaks/Alderis/Alderis.xcodeproj/project.pbxproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,482 @@ | ||||
| // !$*UTF8*$! | ||||
| { | ||||
| 	archiveVersion = 1; | ||||
| 	classes = { | ||||
| 	}; | ||||
| 	objectVersion = 52; | ||||
| 	objects = { | ||||
|  | ||||
| /* Begin PBXBuildFile section */ | ||||
| 		4E1C741328266C5900227EC3 /* UIFontDescriptorAdditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1C741228266C5900227EC3 /* UIFontDescriptorAdditions.swift */; }; | ||||
| 		4E1C74172826C1F100227EC3 /* ColorPickerAccessibilityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1C74162826C1F100227EC3 /* ColorPickerAccessibilityViewController.swift */; }; | ||||
| 		4E1C741928276D5600227EC3 /* TextViewLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1C741828276D5600227EC3 /* TextViewLabel.swift */; }; | ||||
| 		4E1C741B2827829E00227EC3 /* AccessibilityComplianceLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1C741A2827829E00227EC3 /* AccessibilityComplianceLabel.swift */; }; | ||||
| 		4E1C741D2827882600227EC3 /* AccessibilityContrastSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1C741C2827882600227EC3 /* AccessibilityContrastSelector.swift */; }; | ||||
| 		4E1C741F2827B3C800227EC3 /* UIFloat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1C741E2827B3C800227EC3 /* UIFloat.swift */; }; | ||||
| 		4E1C74212827B8F800227EC3 /* NSBeep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1C74202827B8F800227EC3 /* NSBeep.swift */; }; | ||||
| 		4E2E6C06282BD5990089E4FB /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2E6C05282BD5990089E4FB /* GradientView.swift */; }; | ||||
| 		569C25522427F57000022C60 /* ColorPickerTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C25512427F57000022C60 /* ColorPickerTabViewController.swift */; }; | ||||
| 		569C25582428346900022C60 /* ColorPickerMapSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C25572428346900022C60 /* ColorPickerMapSlider.swift */; }; | ||||
| 		56C74667242F722A003ED00A /* ColorPickerSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C74666242F722A003ED00A /* ColorPickerSlider.swift */; }; | ||||
| 		56C74669242F75E3003ED00A /* ColorPickerNumericSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C74668242F75E3003ED00A /* ColorPickerNumericSlider.swift */; }; | ||||
| 		94A2368C252B5951002B5D0B /* UIColorAdditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94A2368B252B5951002B5D0B /* UIColorAdditions.swift */; }; | ||||
| 		CF73D33A241F9C23000B1B10 /* Alderis.h in Headers */ = {isa = PBXBuildFile; fileRef = CF73D338241F9C23000B1B10 /* Alderis.h */; settings = {ATTRIBUTES = (Public, ); }; }; | ||||
| 		CF73D344241F9C31000B1B10 /* ColorWell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF73D32D241E49EE000B1B10 /* ColorWell.swift */; }; | ||||
| 		CF73D345241F9C31000B1B10 /* ColorPickerMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE70280241CBDE700083903 /* ColorPickerMapViewController.swift */; }; | ||||
| 		CF73D346241F9C31000B1B10 /* ColorPickerSlidersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE70282241CC88D00083903 /* ColorPickerSlidersViewController.swift */; }; | ||||
| 		CF73D347241F9C31000B1B10 /* ColorPickerWheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE70288241D0E7300083903 /* ColorPickerWheelView.swift */; }; | ||||
| 		CF73D348241F9C31000B1B10 /* ColorPickerInnerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE7027B241A62ED00083903 /* ColorPickerInnerViewController.swift */; }; | ||||
| 		CF73D34A241F9C31000B1B10 /* ColorPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE70273241A4C5500083903 /* ColorPickerViewController.swift */; }; | ||||
| 		CF73D34B241F9C31000B1B10 /* ColorPickerSwatchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE7027E241B984600083903 /* ColorPickerSwatchViewController.swift */; }; | ||||
| 		CF73D34C241F9C4D000B1B10 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF73D32B241E4720000B1B10 /* Color.swift */; }; | ||||
| 		CF73D351241F9FB3000B1B10 /* ColorPickerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF73D350241F9FB3000B1B10 /* ColorPickerDelegate.swift */; }; | ||||
| 		CF7750B72520D3B50069CC57 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF7750B62520D3B50069CC57 /* Assets.swift */; }; | ||||
| 		CF7750BE252203630069CC57 /* DialogButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF7750BD252203630069CC57 /* DialogButton.swift */; }; | ||||
| 		CF7750CF252433680069CC57 /* ColorPickerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF7750CE252433680069CC57 /* ColorPickerConfiguration.swift */; }; | ||||
| 		CF7750D62524615D0069CC57 /* SeparatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF7750D52524615D0069CC57 /* SeparatorView.swift */; }; | ||||
| 		CF775122252852460069CC57 /* AlderisSDKCompatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = CF775121252852110069CC57 /* AlderisSDKCompatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; | ||||
| 		CF79DA31251723C500F17BCB /* BottomSheetTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF79DA30251723C500F17BCB /* BottomSheetTransition.swift */; }; | ||||
| 		CFAFFC9124277CEE005AD4C1 /* Assets-ios12.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CFAFFC9024277CEE005AD4C1 /* Assets-ios12.xcassets */; platformFilter = ios; }; | ||||
| /* End PBXBuildFile section */ | ||||
|  | ||||
| /* Begin PBXFileReference section */ | ||||
| 		4E1C741228266C5900227EC3 /* UIFontDescriptorAdditions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFontDescriptorAdditions.swift; sourceTree = "<group>"; }; | ||||
| 		4E1C74162826C1F100227EC3 /* ColorPickerAccessibilityViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerAccessibilityViewController.swift; sourceTree = "<group>"; }; | ||||
| 		4E1C741828276D5600227EC3 /* TextViewLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewLabel.swift; sourceTree = "<group>"; }; | ||||
| 		4E1C741A2827829E00227EC3 /* AccessibilityComplianceLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityComplianceLabel.swift; sourceTree = "<group>"; }; | ||||
| 		4E1C741C2827882600227EC3 /* AccessibilityContrastSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityContrastSelector.swift; sourceTree = "<group>"; }; | ||||
| 		4E1C741E2827B3C800227EC3 /* UIFloat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFloat.swift; sourceTree = "<group>"; }; | ||||
| 		4E1C74202827B8F800227EC3 /* NSBeep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSBeep.swift; sourceTree = "<group>"; }; | ||||
| 		4E2E6C05282BD5990089E4FB /* GradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = "<group>"; }; | ||||
| 		569C25512427F57000022C60 /* ColorPickerTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerTabViewController.swift; sourceTree = "<group>"; }; | ||||
| 		569C25572428346900022C60 /* ColorPickerMapSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerMapSlider.swift; sourceTree = "<group>"; }; | ||||
| 		56C74666242F722A003ED00A /* ColorPickerSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerSlider.swift; sourceTree = "<group>"; }; | ||||
| 		56C74668242F75E3003ED00A /* ColorPickerNumericSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerNumericSlider.swift; sourceTree = "<group>"; }; | ||||
| 		94A2368B252B5951002B5D0B /* UIColorAdditions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorAdditions.swift; sourceTree = "<group>"; }; | ||||
| 		CF73D32B241E4720000B1B10 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; }; | ||||
| 		CF73D32D241E49EE000B1B10 /* ColorWell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorWell.swift; sourceTree = "<group>"; }; | ||||
| 		CF73D336241F9C23000B1B10 /* Alderis.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alderis.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| 		CF73D338241F9C23000B1B10 /* Alderis.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Alderis.h; sourceTree = "<group>"; }; | ||||
| 		CF73D339241F9C23000B1B10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||||
| 		CF73D350241F9FB3000B1B10 /* ColorPickerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerDelegate.swift; sourceTree = "<group>"; }; | ||||
| 		CF7750B62520D3B50069CC57 /* Assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = "<group>"; }; | ||||
| 		CF7750BD252203630069CC57 /* DialogButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialogButton.swift; sourceTree = "<group>"; }; | ||||
| 		CF7750CE252433680069CC57 /* ColorPickerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorPickerConfiguration.swift; sourceTree = "<group>"; }; | ||||
| 		CF7750D52524615D0069CC57 /* SeparatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeparatorView.swift; sourceTree = "<group>"; }; | ||||
| 		CF775121252852110069CC57 /* AlderisSDKCompatibility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AlderisSDKCompatibility.h; sourceTree = "<group>"; }; | ||||
| 		CF79DA30251723C500F17BCB /* BottomSheetTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomSheetTransition.swift; sourceTree = "<group>"; }; | ||||
| 		CFAFFC9024277CEE005AD4C1 /* Assets-ios12.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Assets-ios12.xcassets"; sourceTree = "<group>"; }; | ||||
| 		CFE70273241A4C5500083903 /* ColorPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerViewController.swift; sourceTree = "<group>"; }; | ||||
| 		CFE7027B241A62ED00083903 /* ColorPickerInnerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerInnerViewController.swift; sourceTree = "<group>"; }; | ||||
| 		CFE7027E241B984600083903 /* ColorPickerSwatchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerSwatchViewController.swift; sourceTree = "<group>"; }; | ||||
| 		CFE70280241CBDE700083903 /* ColorPickerMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerMapViewController.swift; sourceTree = "<group>"; }; | ||||
| 		CFE70282241CC88D00083903 /* ColorPickerSlidersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerSlidersViewController.swift; sourceTree = "<group>"; }; | ||||
| 		CFE70288241D0E7300083903 /* ColorPickerWheelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerWheelView.swift; sourceTree = "<group>"; }; | ||||
| /* End PBXFileReference section */ | ||||
|  | ||||
| /* Begin PBXFrameworksBuildPhase section */ | ||||
| 		CF73D333241F9C23000B1B10 /* Frameworks */ = { | ||||
| 			isa = PBXFrameworksBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXFrameworksBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXGroup section */ | ||||
| 		CF39D435222BC07C001EF57F = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				CF73D337241F9C23000B1B10 /* Alderis */, | ||||
| 				CF39D43F222BC07C001EF57F /* Products */, | ||||
| 			); | ||||
| 			indentWidth = 2; | ||||
| 			sourceTree = "<group>"; | ||||
| 			tabWidth = 2; | ||||
| 			usesTabs = 1; | ||||
| 		}; | ||||
| 		CF39D43F222BC07C001EF57F /* Products */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				CF73D336241F9C23000B1B10 /* Alderis.framework */, | ||||
| 			); | ||||
| 			name = Products; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		CF73D337241F9C23000B1B10 /* Alderis */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				CF73D338241F9C23000B1B10 /* Alderis.h */, | ||||
| 				CF775121252852110069CC57 /* AlderisSDKCompatibility.h */, | ||||
| 				CF7750B62520D3B50069CC57 /* Assets.swift */, | ||||
| 				CF79DA30251723C500F17BCB /* BottomSheetTransition.swift */, | ||||
| 				CF73D32B241E4720000B1B10 /* Color.swift */, | ||||
| 				CFE70273241A4C5500083903 /* ColorPickerViewController.swift */, | ||||
| 				CF7750CE252433680069CC57 /* ColorPickerConfiguration.swift */, | ||||
| 				CF73D350241F9FB3000B1B10 /* ColorPickerDelegate.swift */, | ||||
| 				CFE7027B241A62ED00083903 /* ColorPickerInnerViewController.swift */, | ||||
| 				569C25512427F57000022C60 /* ColorPickerTabViewController.swift */, | ||||
| 				56C74666242F722A003ED00A /* ColorPickerSlider.swift */, | ||||
| 				CFE7027E241B984600083903 /* ColorPickerSwatchViewController.swift */, | ||||
| 				569C25572428346900022C60 /* ColorPickerMapSlider.swift */, | ||||
| 				CFE70280241CBDE700083903 /* ColorPickerMapViewController.swift */, | ||||
| 				CFE70288241D0E7300083903 /* ColorPickerWheelView.swift */, | ||||
| 				CFE70282241CC88D00083903 /* ColorPickerSlidersViewController.swift */, | ||||
| 				56C74668242F75E3003ED00A /* ColorPickerNumericSlider.swift */, | ||||
| 				4E1C74162826C1F100227EC3 /* ColorPickerAccessibilityViewController.swift */, | ||||
| 				4E1C741A2827829E00227EC3 /* AccessibilityComplianceLabel.swift */, | ||||
| 				4E1C741C2827882600227EC3 /* AccessibilityContrastSelector.swift */, | ||||
| 				CF73D32D241E49EE000B1B10 /* ColorWell.swift */, | ||||
| 				CF7750BD252203630069CC57 /* DialogButton.swift */, | ||||
| 				CF7750D52524615D0069CC57 /* SeparatorView.swift */, | ||||
| 				4E2E6C05282BD5990089E4FB /* GradientView.swift */, | ||||
| 				94A2368B252B5951002B5D0B /* UIColorAdditions.swift */, | ||||
| 				4E1C741228266C5900227EC3 /* UIFontDescriptorAdditions.swift */, | ||||
| 				4E1C741E2827B3C800227EC3 /* UIFloat.swift */, | ||||
| 				4E1C741828276D5600227EC3 /* TextViewLabel.swift */, | ||||
| 				4E1C74202827B8F800227EC3 /* NSBeep.swift */, | ||||
| 				CF73D339241F9C23000B1B10 /* Info.plist */, | ||||
| 				CFAFFC9024277CEE005AD4C1 /* Assets-ios12.xcassets */, | ||||
| 			); | ||||
| 			path = Alderis; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| /* End PBXGroup section */ | ||||
|  | ||||
| /* Begin PBXHeadersBuildPhase section */ | ||||
| 		CF73D331241F9C23000B1B10 /* Headers */ = { | ||||
| 			isa = PBXHeadersBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				CF73D33A241F9C23000B1B10 /* Alderis.h in Headers */, | ||||
| 				CF775122252852460069CC57 /* AlderisSDKCompatibility.h in Headers */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXHeadersBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXNativeTarget section */ | ||||
| 		CF73D335241F9C23000B1B10 /* Alderis */ = { | ||||
| 			isa = PBXNativeTarget; | ||||
| 			buildConfigurationList = CF73D340241F9C23000B1B10 /* Build configuration list for PBXNativeTarget "Alderis" */; | ||||
| 			buildPhases = ( | ||||
| 				CF73D331241F9C23000B1B10 /* Headers */, | ||||
| 				CF73D332241F9C23000B1B10 /* Sources */, | ||||
| 				CF73D333241F9C23000B1B10 /* Frameworks */, | ||||
| 				CF73D334241F9C23000B1B10 /* Resources */, | ||||
| 				CF77511225281F7E0069CC57 /* SwiftLint */, | ||||
| 			); | ||||
| 			buildRules = ( | ||||
| 			); | ||||
| 			dependencies = ( | ||||
| 			); | ||||
| 			name = Alderis; | ||||
| 			productName = Alderis; | ||||
| 			productReference = CF73D336241F9C23000B1B10 /* Alderis.framework */; | ||||
| 			productType = "com.apple.product-type.framework"; | ||||
| 		}; | ||||
| /* End PBXNativeTarget section */ | ||||
|  | ||||
| /* Begin PBXProject section */ | ||||
| 		CF39D436222BC07C001EF57F /* Project object */ = { | ||||
| 			isa = PBXProject; | ||||
| 			attributes = { | ||||
| 				LastUpgradeCheck = 1330; | ||||
| 				ORGANIZATIONNAME = "HASHBANG Productions"; | ||||
| 				TargetAttributes = { | ||||
| 					CF73D335241F9C23000B1B10 = { | ||||
| 						CreatedOnToolsVersion = 11.3.1; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 			buildConfigurationList = CF39D439222BC07C001EF57F /* Build configuration list for PBXProject "Alderis" */; | ||||
| 			compatibilityVersion = "Xcode 9.3"; | ||||
| 			developmentRegion = en; | ||||
| 			hasScannedForEncodings = 0; | ||||
| 			knownRegions = ( | ||||
| 				en, | ||||
| 				Base, | ||||
| 			); | ||||
| 			mainGroup = CF39D435222BC07C001EF57F; | ||||
| 			productRefGroup = CF39D43F222BC07C001EF57F /* Products */; | ||||
| 			projectDirPath = ""; | ||||
| 			projectRoot = ""; | ||||
| 			targets = ( | ||||
| 				CF73D335241F9C23000B1B10 /* Alderis */, | ||||
| 			); | ||||
| 		}; | ||||
| /* End PBXProject section */ | ||||
|  | ||||
| /* Begin PBXResourcesBuildPhase section */ | ||||
| 		CF73D334241F9C23000B1B10 /* Resources */ = { | ||||
| 			isa = PBXResourcesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				CFAFFC9124277CEE005AD4C1 /* Assets-ios12.xcassets in Resources */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXResourcesBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXShellScriptBuildPhase section */ | ||||
| 		CF77511225281F7E0069CC57 /* SwiftLint */ = { | ||||
| 			isa = PBXShellScriptBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			inputFileListPaths = ( | ||||
| 			); | ||||
| 			inputPaths = ( | ||||
| 			); | ||||
| 			name = SwiftLint; | ||||
| 			outputFileListPaths = ( | ||||
| 			); | ||||
| 			outputPaths = ( | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 			shellPath = /bin/sh; | ||||
| 			shellScript = "export PATH=$PATH:/opt/homebrew/bin\nif which swiftlint >/dev/null; then\n\tswiftlint\nelse\n\techo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; | ||||
| 		}; | ||||
| /* End PBXShellScriptBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXSourcesBuildPhase section */ | ||||
| 		CF73D332241F9C23000B1B10 /* Sources */ = { | ||||
| 			isa = PBXSourcesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				CF73D34B241F9C31000B1B10 /* ColorPickerSwatchViewController.swift in Sources */, | ||||
| 				CF7750BE252203630069CC57 /* DialogButton.swift in Sources */, | ||||
| 				4E1C741928276D5600227EC3 /* TextViewLabel.swift in Sources */, | ||||
| 				CF73D347241F9C31000B1B10 /* ColorPickerWheelView.swift in Sources */, | ||||
| 				CF7750D62524615D0069CC57 /* SeparatorView.swift in Sources */, | ||||
| 				CF73D34C241F9C4D000B1B10 /* Color.swift in Sources */, | ||||
| 				94A2368C252B5951002B5D0B /* UIColorAdditions.swift in Sources */, | ||||
| 				569C25582428346900022C60 /* ColorPickerMapSlider.swift in Sources */, | ||||
| 				4E2E6C06282BD5990089E4FB /* GradientView.swift in Sources */, | ||||
| 				CF73D344241F9C31000B1B10 /* ColorWell.swift in Sources */, | ||||
| 				CF7750B72520D3B50069CC57 /* Assets.swift in Sources */, | ||||
| 				CF73D348241F9C31000B1B10 /* ColorPickerInnerViewController.swift in Sources */, | ||||
| 				56C74667242F722A003ED00A /* ColorPickerSlider.swift in Sources */, | ||||
| 				4E1C74212827B8F800227EC3 /* NSBeep.swift in Sources */, | ||||
| 				4E1C74172826C1F100227EC3 /* ColorPickerAccessibilityViewController.swift in Sources */, | ||||
| 				4E1C741D2827882600227EC3 /* AccessibilityContrastSelector.swift in Sources */, | ||||
| 				CF73D345241F9C31000B1B10 /* ColorPickerMapViewController.swift in Sources */, | ||||
| 				CF79DA31251723C500F17BCB /* BottomSheetTransition.swift in Sources */, | ||||
| 				4E1C741328266C5900227EC3 /* UIFontDescriptorAdditions.swift in Sources */, | ||||
| 				CF73D34A241F9C31000B1B10 /* ColorPickerViewController.swift in Sources */, | ||||
| 				CF7750CF252433680069CC57 /* ColorPickerConfiguration.swift in Sources */, | ||||
| 				56C74669242F75E3003ED00A /* ColorPickerNumericSlider.swift in Sources */, | ||||
| 				569C25522427F57000022C60 /* ColorPickerTabViewController.swift in Sources */, | ||||
| 				4E1C741F2827B3C800227EC3 /* UIFloat.swift in Sources */, | ||||
| 				CF73D351241F9FB3000B1B10 /* ColorPickerDelegate.swift in Sources */, | ||||
| 				CF73D346241F9C31000B1B10 /* ColorPickerSlidersViewController.swift in Sources */, | ||||
| 				4E1C741B2827829E00227EC3 /* AccessibilityComplianceLabel.swift in Sources */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXSourcesBuildPhase section */ | ||||
|  | ||||
| /* Begin XCBuildConfiguration section */ | ||||
| 		CF39D452222BC07E001EF57F /* Debug */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | ||||
| 				CLANG_ENABLE_OBJC_WEAK = YES; | ||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||
| 				CLANG_WARN_BOOL_CONVERSION = YES; | ||||
| 				CLANG_WARN_COMMA = YES; | ||||
| 				CLANG_WARN_CONSTANT_CONVERSION = YES; | ||||
| 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||||
| 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||||
| 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||||
| 				CLANG_WARN_EMPTY_BODY = YES; | ||||
| 				CLANG_WARN_ENUM_CONVERSION = YES; | ||||
| 				CLANG_WARN_INFINITE_RECURSION = YES; | ||||
| 				CLANG_WARN_INT_CONVERSION = YES; | ||||
| 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||||
| 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||||
| 				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; | ||||
| 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||||
| 				CLANG_WARN_STRICT_PROTOTYPES = YES; | ||||
| 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||||
| 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | ||||
| 				CLANG_WARN_UNREACHABLE_CODE = YES; | ||||
| 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||||
| 				CODE_SIGN_IDENTITY = "iPhone Developer"; | ||||
| 				COPY_PHASE_STRIP = NO; | ||||
| 				DEBUG_INFORMATION_FORMAT = dwarf; | ||||
| 				ENABLE_STRICT_OBJC_MSGSEND = YES; | ||||
| 				ENABLE_TESTABILITY = YES; | ||||
| 				GCC_C_LANGUAGE_STANDARD = gnu11; | ||||
| 				GCC_DYNAMIC_NO_PIC = NO; | ||||
| 				GCC_NO_COMMON_BLOCKS = YES; | ||||
| 				GCC_OPTIMIZATION_LEVEL = 0; | ||||
| 				GCC_PREPROCESSOR_DEFINITIONS = ( | ||||
| 					"DEBUG=1", | ||||
| 					"$(inherited)", | ||||
| 				); | ||||
| 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||||
| 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||||
| 				GCC_WARN_UNDECLARED_SELECTOR = YES; | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; | ||||
| 				MTL_FAST_MATH = YES; | ||||
| 				ONLY_ACTIVE_ARCH = YES; | ||||
| 				SDKROOT = iphoneos; | ||||
| 			}; | ||||
| 			name = Debug; | ||||
| 		}; | ||||
| 		CF39D453222BC07E001EF57F /* Release */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | ||||
| 				CLANG_ENABLE_OBJC_WEAK = YES; | ||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||
| 				CLANG_WARN_BOOL_CONVERSION = YES; | ||||
| 				CLANG_WARN_COMMA = YES; | ||||
| 				CLANG_WARN_CONSTANT_CONVERSION = YES; | ||||
| 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||||
| 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||||
| 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||||
| 				CLANG_WARN_EMPTY_BODY = YES; | ||||
| 				CLANG_WARN_ENUM_CONVERSION = YES; | ||||
| 				CLANG_WARN_INFINITE_RECURSION = YES; | ||||
| 				CLANG_WARN_INT_CONVERSION = YES; | ||||
| 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||||
| 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||||
| 				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; | ||||
| 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||||
| 				CLANG_WARN_STRICT_PROTOTYPES = YES; | ||||
| 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||||
| 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | ||||
| 				CLANG_WARN_UNREACHABLE_CODE = YES; | ||||
| 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||||
| 				CODE_SIGN_IDENTITY = "iPhone Developer"; | ||||
| 				COPY_PHASE_STRIP = NO; | ||||
| 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||||
| 				ENABLE_NS_ASSERTIONS = NO; | ||||
| 				ENABLE_STRICT_OBJC_MSGSEND = YES; | ||||
| 				GCC_C_LANGUAGE_STANDARD = gnu11; | ||||
| 				GCC_NO_COMMON_BLOCKS = YES; | ||||
| 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||||
| 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||||
| 				GCC_WARN_UNDECLARED_SELECTOR = YES; | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = NO; | ||||
| 				MTL_FAST_MATH = YES; | ||||
| 				SDKROOT = iphoneos; | ||||
| 				SWIFT_COMPILATION_MODE = wholemodule; | ||||
| 				VALIDATE_PRODUCT = YES; | ||||
| 			}; | ||||
| 			name = Release; | ||||
| 		}; | ||||
| 		CF73D341241F9C23000B1B10 /* Debug */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				APPLICATION_EXTENSION_API_ONLY = YES; | ||||
| 				BUILD_LIBRARY_FOR_DISTRIBUTION = YES; | ||||
| 				CURRENT_PROJECT_VERSION = 3; | ||||
| 				DEFINES_MODULE = YES; | ||||
| 				DYLIB_COMPATIBILITY_VERSION = 1; | ||||
| 				DYLIB_CURRENT_VERSION = 1; | ||||
| 				DYLIB_INSTALL_NAME_BASE = "@rpath"; | ||||
| 				INFOPLIST_FILE = Alderis/Info.plist; | ||||
| 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 					"@loader_path/Frameworks", | ||||
| 				); | ||||
| 				MARKETING_VERSION = 1.2; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = ws.hbang.Alderis; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; | ||||
| 				SKIP_INSTALL = NO; | ||||
| 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; | ||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TARGETED_DEVICE_FAMILY = "1,2"; | ||||
| 				VERSIONING_SYSTEM = "apple-generic"; | ||||
| 				VERSION_INFO_PREFIX = ""; | ||||
| 			}; | ||||
| 			name = Debug; | ||||
| 		}; | ||||
| 		CF73D342241F9C23000B1B10 /* Release */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				APPLICATION_EXTENSION_API_ONLY = YES; | ||||
| 				BUILD_LIBRARY_FOR_DISTRIBUTION = YES; | ||||
| 				CURRENT_PROJECT_VERSION = 3; | ||||
| 				DEFINES_MODULE = YES; | ||||
| 				DYLIB_COMPATIBILITY_VERSION = 1; | ||||
| 				DYLIB_CURRENT_VERSION = 1; | ||||
| 				DYLIB_INSTALL_NAME_BASE = "@rpath"; | ||||
| 				INFOPLIST_FILE = Alderis/Info.plist; | ||||
| 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 					"@loader_path/Frameworks", | ||||
| 				); | ||||
| 				MARKETING_VERSION = 1.2; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = ws.hbang.Alderis; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; | ||||
| 				SKIP_INSTALL = NO; | ||||
| 				SWIFT_COMPILATION_MODE = wholemodule; | ||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-O"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TARGETED_DEVICE_FAMILY = "1,2"; | ||||
| 				VERSIONING_SYSTEM = "apple-generic"; | ||||
| 				VERSION_INFO_PREFIX = ""; | ||||
| 			}; | ||||
| 			name = Release; | ||||
| 		}; | ||||
| /* End XCBuildConfiguration section */ | ||||
|  | ||||
| /* Begin XCConfigurationList section */ | ||||
| 		CF39D439222BC07C001EF57F /* Build configuration list for PBXProject "Alderis" */ = { | ||||
| 			isa = XCConfigurationList; | ||||
| 			buildConfigurations = ( | ||||
| 				CF39D452222BC07E001EF57F /* Debug */, | ||||
| 				CF39D453222BC07E001EF57F /* Release */, | ||||
| 			); | ||||
| 			defaultConfigurationIsVisible = 0; | ||||
| 			defaultConfigurationName = Release; | ||||
| 		}; | ||||
| 		CF73D340241F9C23000B1B10 /* Build configuration list for PBXNativeTarget "Alderis" */ = { | ||||
| 			isa = XCConfigurationList; | ||||
| 			buildConfigurations = ( | ||||
| 				CF73D341241F9C23000B1B10 /* Debug */, | ||||
| 				CF73D342241F9C23000B1B10 /* Release */, | ||||
| 			); | ||||
| 			defaultConfigurationIsVisible = 0; | ||||
| 			defaultConfigurationName = Release; | ||||
| 		}; | ||||
| /* End XCConfigurationList section */ | ||||
| 	}; | ||||
| 	rootObject = CF39D436222BC07C001EF57F /* Project object */; | ||||
| } | ||||
							
								
								
									
										7
									
								
								Tweaks/Alderis/Alderis.xcodeproj/project.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
|    <FileRef | ||||
|       location = "self:Alderis Demo.xcodeproj"> | ||||
|    </FileRef> | ||||
| </Workspace> | ||||
| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>IDEDidComputeMac32BitWarning</key> | ||||
| 	<true/> | ||||
| </dict> | ||||
| </plist> | ||||
| @@ -0,0 +1,67 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Scheme | ||||
|    LastUpgradeVersion = "1330" | ||||
|    version = "1.3"> | ||||
|    <BuildAction | ||||
|       parallelizeBuildables = "YES" | ||||
|       buildImplicitDependencies = "YES"> | ||||
|       <BuildActionEntries> | ||||
|          <BuildActionEntry | ||||
|             buildForTesting = "YES" | ||||
|             buildForRunning = "YES" | ||||
|             buildForProfiling = "YES" | ||||
|             buildForArchiving = "YES" | ||||
|             buildForAnalyzing = "YES"> | ||||
|             <BuildableReference | ||||
|                BuildableIdentifier = "primary" | ||||
|                BlueprintIdentifier = "CF73D335241F9C23000B1B10" | ||||
|                BuildableName = "Alderis.framework" | ||||
|                BlueprintName = "Alderis" | ||||
|                ReferencedContainer = "container:Alderis.xcodeproj"> | ||||
|             </BuildableReference> | ||||
|          </BuildActionEntry> | ||||
|       </BuildActionEntries> | ||||
|    </BuildAction> | ||||
|    <TestAction | ||||
|       buildConfiguration = "Debug" | ||||
|       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||||
|       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||||
|       shouldUseLaunchSchemeArgsEnv = "YES"> | ||||
|       <Testables> | ||||
|       </Testables> | ||||
|    </TestAction> | ||||
|    <LaunchAction | ||||
|       buildConfiguration = "Debug" | ||||
|       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||||
|       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||||
|       launchStyle = "0" | ||||
|       useCustomWorkingDirectory = "NO" | ||||
|       ignoresPersistentStateOnLaunch = "NO" | ||||
|       debugDocumentVersioning = "YES" | ||||
|       debugServiceExtension = "internal" | ||||
|       allowLocationSimulation = "YES"> | ||||
|    </LaunchAction> | ||||
|    <ProfileAction | ||||
|       buildConfiguration = "Release" | ||||
|       shouldUseLaunchSchemeArgsEnv = "YES" | ||||
|       savedToolIdentifier = "" | ||||
|       useCustomWorkingDirectory = "NO" | ||||
|       debugDocumentVersioning = "YES"> | ||||
|       <MacroExpansion> | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "CF73D335241F9C23000B1B10" | ||||
|             BuildableName = "Alderis.framework" | ||||
|             BlueprintName = "Alderis" | ||||
|             ReferencedContainer = "container:Alderis.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|       </MacroExpansion> | ||||
|    </ProfileAction> | ||||
|    <AnalyzeAction | ||||
|       buildConfiguration = "Debug"> | ||||
|    </AnalyzeAction> | ||||
|    <ArchiveAction | ||||
|       buildConfiguration = "Release" | ||||
|       revealArchiveInOrganizer = "YES"> | ||||
|    </ArchiveAction> | ||||
| </Scheme> | ||||
							
								
								
									
										77
									
								
								Tweaks/Alderis/Alderis/AccessibilityComplianceLabel.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,77 @@ | ||||
| // | ||||
| //  AccessibilityComplianceLabel.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 8/5/2022. | ||||
| //  Copyright © 2022 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class AccessibilityComplianceLabel: UIView { | ||||
|  | ||||
| 	var text: String { | ||||
| 		get { label.text! } | ||||
| 		set { label.text = newValue } | ||||
| 	} | ||||
|  | ||||
| 	var isCompliant = false { | ||||
| 		didSet { updateState() } | ||||
| 	} | ||||
|  | ||||
| 	private let tickImage  = Assets.systemImage(named: "checkmark.circle.fill") | ||||
| 	private let crossImage = Assets.systemImage(named: "xmark.circle.fill") | ||||
|  | ||||
| 	private var imageView: UIImageView! | ||||
| 	private var label: UILabel! | ||||
|  | ||||
| 	override init(frame: CGRect) { | ||||
| 		super.init(frame: frame) | ||||
|  | ||||
| 		let font = UIFont.systemFont(ofSize: UIFloat(16), weight: .medium) | ||||
|  | ||||
| 		imageView = UIImageView() | ||||
| 		if #available(iOS 13, *) { | ||||
| 			imageView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(font: font, scale: .small) | ||||
| 		} | ||||
|  | ||||
| 		label = UILabel() | ||||
| 		label.font = font | ||||
|  | ||||
| 		let stackView = UIStackView(arrangedSubviews: [imageView, label]) | ||||
| 		stackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		stackView.alignment = .center | ||||
| 		stackView.spacing = UIFloat(6) | ||||
| 		addSubview(stackView) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			stackView.topAnchor.constraint(equalTo: self.topAnchor), | ||||
| 			stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor), | ||||
| 			stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor), | ||||
| 			stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	convenience init(text: String) { | ||||
| 		self.init(frame: .zero) | ||||
| 		self.text = text | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	private func updateState() { | ||||
| 		let color = isCompliant ? Assets.green : Assets.red | ||||
|  | ||||
| 		tintColor = color | ||||
| 		accessibilityLabel = "\(text): \(isCompliant ? "Compliant" : "Not compliant")" | ||||
| 		imageView.image = isCompliant ? tickImage : crossImage | ||||
| 	} | ||||
|  | ||||
| 	override func tintColorDidChange() { | ||||
| 		super.tintColorDidChange() | ||||
| 		label.textColor = tintColor | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										91
									
								
								Tweaks/Alderis/Alderis/AccessibilityContrastSelector.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | ||||
| // | ||||
| //  AccessibilityContrastSelector.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 8/5/2022. | ||||
| //  Copyright © 2022 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class AccessibilityContrastSelector: UIView { | ||||
|  | ||||
| 	enum Mode: Int, CaseIterable { | ||||
| 		case color, black, white | ||||
|  | ||||
| 		var label: String { | ||||
| 			switch self { | ||||
| 			case .color: return "Color" | ||||
| 			case .black: return "Black" | ||||
| 			case .white: return "White" | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		func color(withColor color: Color) -> Color { | ||||
| 			switch self { | ||||
| 			case .color: return color | ||||
| 			case .black: return .black | ||||
| 			case .white: return .white | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var text: String { | ||||
| 		get { label.text! } | ||||
| 		set { label.text = newValue } | ||||
| 	} | ||||
|  | ||||
| 	var value: Mode = .white { | ||||
| 		didSet { | ||||
| 			if segmentedControl.selectedSegmentIndex != value.rawValue { | ||||
| 				segmentedControl.selectedSegmentIndex = value.rawValue | ||||
| 				handleChange?(value) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var handleChange: ((Mode) -> Void)? | ||||
|  | ||||
| 	private var label: UILabel! | ||||
| 	private var segmentedControl: UISegmentedControl! | ||||
|  | ||||
| 	override init(frame: CGRect) { | ||||
| 		super.init(frame: frame) | ||||
|  | ||||
| 		label = UILabel() | ||||
| 		label.font = UIFont.systemFont(ofSize: UIFloat(16), weight: .medium) | ||||
|  | ||||
| 		segmentedControl = UISegmentedControl(items: Mode.allCases.map(\.label)) | ||||
| 		segmentedControl.addTarget(self, action: #selector(handleValueChanged), for: .valueChanged) | ||||
|  | ||||
| 		let stackView = UIStackView(arrangedSubviews: [label, UIView(), segmentedControl]) | ||||
| 		stackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		stackView.alignment = .center | ||||
| 		stackView.distribution = .fill | ||||
| 		stackView.spacing = UIFloat(5) | ||||
| 		addSubview(stackView) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			stackView.topAnchor.constraint(equalTo: self.topAnchor), | ||||
| 			stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor), | ||||
| 			stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor), | ||||
| 			stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	convenience init(text: String, value: Mode) { | ||||
| 		self.init(frame: .zero) | ||||
| 		self.text = text | ||||
| 		self.value = value | ||||
| 	} | ||||
|  | ||||
| 	@objc private func handleValueChanged() { | ||||
| 		value = Mode(rawValue: segmentedControl.selectedSegmentIndex)! | ||||
| 		handleChange?(value) | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										11
									
								
								Tweaks/Alderis/Alderis/Alderis.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,11 @@ | ||||
| // | ||||
| //  Alderis.h | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 16/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| @import UIKit; | ||||
|  | ||||
| #import "AlderisSDKCompatibility.h" | ||||
							
								
								
									
										21
									
								
								Tweaks/Alderis/Alderis/AlderisSDKCompatibility.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
| // | ||||
| //  AlderisSDKCompatibility.h | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 3/10/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| @import UIKit; | ||||
|  | ||||
| #ifndef __IPHONE_14_0 | ||||
| // Allows building with the iOS 13 SDK while retaining iOS 14 compatibility. | ||||
|  | ||||
| @interface UIControl () | ||||
|  | ||||
| - (void)addAction:(UIAction *)action forControlEvents:(UIControlEvents)controlEvents NS_SWIFT_NAME(addAction(_:for:)) API_AVAILABLE(ios(14.0)); | ||||
| - (void)removeAction:(UIAction *)action forControlEvents:(UIControlEvents)controlEvents NS_SWIFT_NAME(removeAction(_:for:)) API_AVAILABLE(ios(14.0)); | ||||
| - (void)removeActionForIdentifier:(UIActionIdentifier)actionIdentifier forControlEvents:(UIControlEvents)controlEvents NS_SWIFT_NAME(removeAction(identifiedBy:for:)) API_AVAILABLE(ios(14.0)); | ||||
|  | ||||
| @end | ||||
| #endif | ||||
| @@ -0,0 +1,6 @@ | ||||
| { | ||||
|   "info" : { | ||||
|     "version" : 1, | ||||
|     "author" : "xcode" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										26
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Brightness Slider/sun.max.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "sun.max.png", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "sun.max@2x.png", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "sun.max@3x.png", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "version" : 1, | ||||
|     "author" : "xcode" | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Brightness Slider/sun.max.imageset/sun.max.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 310 B | 
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Brightness Slider/sun.max.imageset/sun.max@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 661 B | 
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Brightness Slider/sun.max.imageset/sun.max@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 942 B | 
							
								
								
									
										26
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Brightness Slider/sun.min.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "sun.min.png", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "sun.min@2x.png", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "filename" : "sun.min@3x.png", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "version" : 1, | ||||
|     "author" : "xcode" | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Brightness Slider/sun.min.imageset/sun.min.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 307 B | 
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Brightness Slider/sun.min.imageset/sun.min@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 606 B | 
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Brightness Slider/sun.min.imageset/sun.min@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 909 B | 
| @@ -0,0 +1,6 @@ | ||||
| { | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,6 @@ | ||||
| { | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "checkmark.circle.fill.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "checkmark.circle.fill@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "checkmark.circle.fill@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
| After Width: | Height: | Size: 169 B | 
| After Width: | Height: | Size: 287 B | 
| After Width: | Height: | Size: 377 B | 
							
								
								
									
										26
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Contrast Checker/sparkles.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "sparkles.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "sparkles@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "sparkles@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Contrast Checker/sparkles.imageset/sparkles.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 184 B | 
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Contrast Checker/sparkles.imageset/sparkles@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 324 B | 
							
								
								
									
										
											BIN
										
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Contrast Checker/sparkles.imageset/sparkles@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 427 B | 
| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "xmark.circle.fill.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "xmark.circle.fill@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "xmark.circle.fill@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
| After Width: | Height: | Size: 164 B | 
| After Width: | Height: | Size: 274 B | 
| After Width: | Height: | Size: 383 B | 
| @@ -0,0 +1,6 @@ | ||||
| { | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										26
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Tabs/circle.righthalf.fill.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "circle.righthalf.fill.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "circle.righthalf.fill@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "circle.righthalf.fill@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
| After Width: | Height: | Size: 229 B | 
| After Width: | Height: | Size: 385 B | 
| After Width: | Height: | Size: 574 B | 
							
								
								
									
										26
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Tabs/slider.horizontal.3.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "slider.horizontal.3.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "slider.horizontal.3@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "slider.horizontal.3@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
| After Width: | Height: | Size: 208 B | 
| After Width: | Height: | Size: 361 B | 
| After Width: | Height: | Size: 507 B | 
| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "slider.horizontal.below.rectangle.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "slider.horizontal.below.rectangle@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "slider.horizontal.below.rectangle@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
| After Width: | Height: | Size: 189 B | 
| After Width: | Height: | Size: 309 B | 
| After Width: | Height: | Size: 412 B | 
							
								
								
									
										26
									
								
								Tweaks/Alderis/Alderis/Assets-ios12.xcassets/Tabs/square.grid.4x3.fill.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "square.grid.4x3.fill.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "square.grid.4x3.fill@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "square.grid.4x3.fill@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   }, | ||||
|   "properties" : { | ||||
|     "template-rendering-intent" : "template" | ||||
|   } | ||||
| } | ||||
| After Width: | Height: | Size: 211 B | 
| After Width: | Height: | Size: 289 B | 
| After Width: | Height: | Size: 419 B | 
							
								
								
									
										135
									
								
								Tweaks/Alderis/Alderis/Assets.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,135 @@ | ||||
| // | ||||
| //  Assets.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 27/9/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal struct Assets { | ||||
|  | ||||
| 	internal enum SymbolScale: Int { | ||||
| 		case `default` = -1 | ||||
| 		case unspecified, small, medium, large | ||||
|  | ||||
| 		@available(iOS 13, *) | ||||
| 		var uiImageSymbolScale: UIImage.SymbolScale { UIImage.SymbolScale(rawValue: rawValue)! } | ||||
| 	} | ||||
|  | ||||
| 	private static let bundle: Bundle = { | ||||
| 		let myBundle = Bundle(for: ColorPickerViewController.self) | ||||
| 		if let resourcesURL = myBundle.url(forResource: "Alderis", withExtension: "bundle"), | ||||
| 			 let resourcesBundle = Bundle(url: resourcesURL) { | ||||
| 			return resourcesBundle | ||||
| 		} | ||||
| 		return myBundle | ||||
| 	}() | ||||
| 	private static let uikitBundle = Bundle(for: UIView.self) | ||||
|  | ||||
| 	// MARK: - Localization | ||||
|  | ||||
| 	static func uikitLocalize(_ key: String) -> String { | ||||
| 		uikitBundle.localizedString(forKey: key, value: nil, table: nil) | ||||
| 	} | ||||
|  | ||||
| 	// MARK: - Images | ||||
|  | ||||
| 	static func systemImage(named name: String, font: UIFont? = nil, scale: SymbolScale = .default) -> UIImage? { | ||||
| 		if #available(iOS 13, *) { | ||||
| 			var configuration: UIImage.SymbolConfiguration? | ||||
| 			if let font = font { | ||||
| 				configuration = UIImage.SymbolConfiguration(font: font, scale: scale.uiImageSymbolScale) | ||||
| 			} | ||||
| 			return UIImage(systemName: name, withConfiguration: configuration) | ||||
| 		} | ||||
| 		return UIImage(named: name, in: bundle, compatibleWith: nil) | ||||
| 	} | ||||
|  | ||||
| 	// MARK: - Fonts | ||||
|  | ||||
| 	static func niceMonospaceDigitFont(ofSize size: CGFloat) -> UIFont { | ||||
| 		// Take the monospace digit font and enable stylistic alternative 6, which provides a | ||||
| 		// high-legibility, monospace-looking style of the system font. | ||||
| 		let font = UIFont.monospacedDigitSystemFont(ofSize: size, weight: .regular) | ||||
| 		let fontDescriptor = font.fontDescriptor.addingAttributes([ | ||||
| 			.featureSettings: [ | ||||
| 				[ | ||||
| 					.alderisFeature: kStylisticAlternativesType, | ||||
| 					.alderisSelector: kStylisticAltSixOnSelector | ||||
| 				] | ||||
| 			] as [[UIFontDescriptor.FeatureKey: Int]] | ||||
| 		]) | ||||
| 		return UIFont(descriptor: fontDescriptor, size: 0) | ||||
| 	} | ||||
|  | ||||
| 	// MARK: - Colors | ||||
|  | ||||
| 	private static func color(userInterfaceStyles colors: [UIUserInterfaceStyle: UIColor], fallback: UIUserInterfaceStyle = .light) -> UIColor { | ||||
| 		if #available(iOS 13, *) { | ||||
| 			return UIColor { colors[$0.userInterfaceStyle] ?? colors[fallback] ?? colors.values.first! } | ||||
| 		} | ||||
| 		return colors[fallback] ?? colors.values.first! | ||||
| 	} | ||||
|  | ||||
| 	static let backdropColor  = UIColor(white: 0, alpha: 0.2) | ||||
| 	static let separatorColor = UIColor(white: 1, alpha: 0.15) | ||||
|  | ||||
| 	static let labelColor: UIColor = { | ||||
| 		if #available(iOS 13, *) { | ||||
| 			return .label | ||||
| 		} | ||||
| 		return .black | ||||
| 	}() | ||||
|  | ||||
| 	static let secondaryLabelColor: UIColor = { | ||||
| 		if #available(iOS 13, *) { | ||||
| 			return .secondaryLabel | ||||
| 		} | ||||
| 		return UIColor(white: 60 / 255, alpha: 0.6) | ||||
| 	}() | ||||
|  | ||||
| 	static let borderColor: UIColor = { | ||||
| 		if #available(iOS 13, *) { | ||||
| 			return .separator | ||||
| 		} | ||||
| 		return UIColor(white: 1, alpha: 0.35) | ||||
| 	}() | ||||
|  | ||||
| 	@available(iOS 13, *) | ||||
| 	static let macTabBarSelectionColor = color(userInterfaceStyles: [ | ||||
| 		.light: .black, | ||||
| 		.dark: .label | ||||
| 	]) | ||||
|  | ||||
| 	static let red = UIColor.systemRed | ||||
|  | ||||
| 	// .systemGreen adjusted to be more consistent / easier to read | ||||
| 	static let green = color(userInterfaceStyles: [ | ||||
| 		.light: UIColor(red: 15 / 255, green: 189 / 255, blue: 59 / 255, alpha: 1), | ||||
| 		// swiftlint:disable:next colon | ||||
| 		.dark:  UIColor(red: 41 / 255, green: 179 / 255, blue: 76 / 255, alpha: 1) | ||||
| 	]) | ||||
|  | ||||
| 	static let checkerboardPatternColor = color(userInterfaceStyles: [ | ||||
| 		.light: renderCheckerboardPattern(colors: (UIColor(white: 200 / 255, alpha: 1), | ||||
| 																							 UIColor(white: 255 / 255, alpha: 1))), | ||||
| 		// swiftlint:disable:next colon | ||||
| 		.dark:  renderCheckerboardPattern(colors: (UIColor(white: 140 / 255, alpha: 1), | ||||
| 																							 UIColor(white: 186 / 255, alpha: 1))) | ||||
| 	]) | ||||
|  | ||||
| 	private static func renderCheckerboardPattern(colors: (dark: UIColor, light: UIColor)) -> UIColor { | ||||
| 		let size = 11 | ||||
| 		let image = UIGraphicsImageRenderer(size: CGSize(width: size * 2, height: size * 2)).image { context in | ||||
| 			colors.dark.setFill() | ||||
| 			context.fill(CGRect(x: 0, y: 0, width: size * 2, height: size * 2)) | ||||
| 			colors.light.setFill() | ||||
| 			context.fill(CGRect(x: size, y: 0, width: size, height: size)) | ||||
| 			context.fill(CGRect(x: 0, y: size, width: size, height: size)) | ||||
| 		} | ||||
| 		return UIColor(patternImage: image) | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										48
									
								
								Tweaks/Alderis/Alderis/BottomSheetTransition.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,48 @@ | ||||
| // | ||||
| //  BottomSheetTransition.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 20/9/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class BottomSheetTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate { | ||||
|  | ||||
| 	func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { | ||||
| 		return BottomSheetTransition(direction: true) | ||||
| 	} | ||||
|  | ||||
| 	func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { | ||||
| 		return BottomSheetTransition(direction: false) | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| internal class BottomSheetTransition: NSObject, UIViewControllerAnimatedTransitioning { | ||||
|  | ||||
| 	// Opening: true | ||||
| 	// Closing: false | ||||
| 	let direction: Bool | ||||
|  | ||||
| 	init(direction: Bool) { | ||||
| 		self.direction = direction | ||||
| 	} | ||||
|  | ||||
| 	func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | ||||
| 		0.4 | ||||
| 	} | ||||
|  | ||||
| 	func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | ||||
| 		if direction { | ||||
| 			let to = transitionContext.viewController(forKey: .to)! | ||||
| 			transitionContext.containerView.addSubview(to.view) | ||||
| 		} | ||||
|  | ||||
| 		Timer.scheduledTimer(withTimeInterval: transitionDuration(using: transitionContext), repeats: false) { _ in | ||||
| 			transitionContext.completeTransition(true) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										342
									
								
								Tweaks/Alderis/Alderis/Color.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,342 @@ | ||||
| // | ||||
| //  Color.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 15/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal struct Color: Equatable, Hashable { | ||||
| 	static let black = Color(white: 0, alpha: 1) | ||||
| 	static let white = Color(white: 1, alpha: 1) | ||||
|  | ||||
| 	var red: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(red: red, green: green, blue: blue, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
| 	var green: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(red: red, green: green, blue: blue, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
| 	var blue: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(red: red, green: green, blue: blue, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var hue: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
| 	var saturation: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
| 	var brightness: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
| 	var hslSaturation: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(hue: hue, saturation: hslSaturation, lightness: lightness, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
| 	var lightness: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(hue: hue, saturation: hslSaturation, lightness: lightness, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var white: CGFloat = 0 { | ||||
| 		didSet { | ||||
| 			self = Color(white: white, alpha: alpha) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var alpha: CGFloat = 0 | ||||
|  | ||||
| 	static func == (lhs: Color, rhs: Color) -> Bool { | ||||
| 		lhs.red == rhs.red && | ||||
| 		lhs.green == rhs.green && | ||||
| 		lhs.blue == rhs.blue && | ||||
| 		lhs.alpha == rhs.alpha | ||||
| 	} | ||||
|  | ||||
| 	func hash(into hasher: inout Hasher) { | ||||
| 		hasher.combine(red) | ||||
| 		hasher.combine(green) | ||||
| 		hasher.combine(blue) | ||||
| 		hasher.combine(alpha) | ||||
| 	} | ||||
|  | ||||
| 	var uiColor: UIColor { .init(red: red, green: green, blue: blue, alpha: alpha) } | ||||
|  | ||||
| 	init(uiColor: UIColor) { | ||||
| 		uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) | ||||
| 		uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil) | ||||
| 		self.white = brightness | ||||
| 		(self.hslSaturation, self.lightness) = hslValue | ||||
| 	} | ||||
|  | ||||
| 	init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { | ||||
| 		self.red = red | ||||
| 		self.green = green | ||||
| 		self.blue = blue | ||||
| 		self.alpha = alpha | ||||
| 		let uiColor = UIColor(red: red, green: green, blue: blue, alpha: alpha) | ||||
| 		uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil) | ||||
| 		self.white = brightness | ||||
| 		(self.hslSaturation, self.lightness) = hslValue | ||||
| 	} | ||||
|  | ||||
| 	init(white: CGFloat, alpha: CGFloat) { | ||||
| 		self.init(red: white, green: white, blue: white, alpha: alpha) | ||||
| 	} | ||||
|  | ||||
| 	init(hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat) { | ||||
| 		self.hue = hue | ||||
| 		self.saturation = saturation | ||||
| 		self.brightness = brightness | ||||
| 		self.white = brightness | ||||
| 		self.alpha = alpha | ||||
| 		let uiColor = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha) | ||||
| 		uiColor.getRed(&red, green: &green, blue: &blue, alpha: nil) | ||||
| 		(self.hslSaturation, self.lightness) = hslValue | ||||
| 	} | ||||
|  | ||||
| 	init(hue: CGFloat, saturation: CGFloat, lightness: CGFloat, alpha: CGFloat) { | ||||
| 		self.hue = hue | ||||
| 		self.hslSaturation = saturation | ||||
| 		self.lightness = lightness | ||||
| 		self.alpha = alpha | ||||
| 		(self.saturation, self.brightness) = hsbValue | ||||
| 		let uiColor = UIColor(hue: hue, saturation: self.saturation, brightness: self.brightness, alpha: alpha) | ||||
| 		uiColor.getRed(&red, green: &green, blue: &blue, alpha: nil) | ||||
| 		self.white = brightness | ||||
| 	} | ||||
| } | ||||
|  | ||||
| extension Color { | ||||
| 	static var brightnessThreshold: CGFloat { | ||||
| 		// Accessibility enabled:  conforms to WCAG 2.1 AAA | ||||
| 		// Accessibility disabled: conforms to WCAG 2.1 AA | ||||
| 		UIAccessibility.isDarkerSystemColorsEnabled ? 7 : 4.5 | ||||
| 	} | ||||
|  | ||||
| 	var relativeLuminanceValues: (red: CGFloat, green: CGFloat, blue: CGFloat) { | ||||
| 		// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance | ||||
| 		let values = [red, green, blue] | ||||
| 			.map { $0 <= 0.03928 ? $0 / 12.92 : pow((($0 + 0.055) / 1.055), 2.4) } | ||||
| 		return (values[0], values[1], values[2]) | ||||
| 	} | ||||
|  | ||||
| 	var relativeLuminance: CGFloat { | ||||
| 		// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance | ||||
| 		let (r, g, b) = relativeLuminanceValues | ||||
| 		return (r * 0.2126) + (g * 0.7152) + (b * 0.0722) | ||||
| 	} | ||||
|  | ||||
| 	func perceivedBrightness(onBackgroundColor background: Color) -> CGFloat { | ||||
| 		// https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio - between 0-21 | ||||
| 		let a = relativeLuminance + 0.05 | ||||
| 		let b = background.relativeLuminance + 0.05 | ||||
| 		return a > b ? a / b : b / a | ||||
| 	} | ||||
|  | ||||
| 	var isDark: Bool { perceivedBrightness(onBackgroundColor: .white) > Self.brightnessThreshold && alpha > 0.5 } | ||||
| } | ||||
|  | ||||
| extension Color { | ||||
| 	struct HexOptions: OptionSet { | ||||
| 		let rawValue: Int | ||||
| 		static let allowShorthand = Self(rawValue: 1 << 0) | ||||
| 		static let forceAlpha = Self(rawValue: 1 << 1) | ||||
| 	} | ||||
|  | ||||
| 	// if the character in `value` is repeated, `repeatedValue` is a single copy of that character. If | ||||
| 	// `value` consists of two unique characters, `repeatedValue` is nil | ||||
| 	// e.g. valid return values are `("AA", "A")` and `("AB", nil)` | ||||
| 	private func hex(_ val: CGFloat) -> (value: String, repeatedValue: Character?) { | ||||
| 		let byte = Int(val * 255) & 0xFF | ||||
| 		let isRepeated = (byte & 0xF) == (byte >> 4) | ||||
| 		let value = String(format: "%02X", byte) | ||||
| 		return (value, isRepeated ? value.first : nil) | ||||
| 	} | ||||
|  | ||||
| 	func hexString(with options: HexOptions = []) -> String { | ||||
| 		let (r, rRep) = hex(red) | ||||
| 		let (g, gRep) = hex(green) | ||||
| 		let (b, bRep) = hex(blue) | ||||
| 		let (a, aRep) = hex(alpha) | ||||
| 		let showAlpha = options.contains(.forceAlpha) || alpha != 1 | ||||
| 		if options.contains(.allowShorthand), | ||||
| 			let rRep = rRep, let gRep = gRep, let bRep = bRep, let aRep = aRep { | ||||
| 			return "#\(rRep)\(gRep)\(bRep)\(showAlpha ? "\(aRep)" : "")" | ||||
| 		} else { | ||||
| 			return "#\(r)\(g)\(b)\(showAlpha ? a : "")" | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var hexString: String { hexString() } | ||||
|  | ||||
| 	var hslValue: (saturation: CGFloat, lightness: CGFloat) { | ||||
| 		let lightness = brightness - (brightness * (saturation / 2)) | ||||
| 		var saturation = min(lightness, 1 - lightness) | ||||
| 		saturation = saturation == 0 ? 0 : (brightness - lightness) / saturation | ||||
| 		return (saturation, lightness) | ||||
| 	} | ||||
|  | ||||
| 	var hsbValue: (saturation: CGFloat, brightness: CGFloat) { | ||||
| 		let brightness = hslSaturation * min(lightness, 1 - lightness) + lightness | ||||
| 		let saturation = brightness == 0 ? 0 : 2 - 2 * lightness / brightness | ||||
| 		return (saturation, brightness) | ||||
| 	} | ||||
|  | ||||
| 	private func cssString(function: String, params: [String?]) -> String { | ||||
| 		let filteredParams = params.compactMap { $0 } | ||||
| 		return "\(function)\(filteredParams.count == 4 ? "a" : "")(\(filteredParams.joined(separator: ", ")))" | ||||
| 	} | ||||
|  | ||||
| 	var rgbString: String { | ||||
| 		cssString(function: "rgb", params: [ | ||||
| 			"\(Int(red * 255))", | ||||
| 			"\(Int(green * 255))", | ||||
| 			"\(Int(blue * 255))", | ||||
| 			alpha == 1 ? nil : String(format: "%.2f", alpha) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	var hslString: String { | ||||
| 		cssString(function: "hsl", params: [ | ||||
| 			"\(Int(hue * 360))", | ||||
| 			"\(Int(hslSaturation * 100))%", | ||||
| 			"\(Int(lightness * 100))%", | ||||
| 			alpha == 1 ? nil : String(format: "%.2f", alpha) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	var objcString: String { | ||||
| 		red == green && green == blue | ||||
| 			? String(format: "[UIColor colorWithWhite:%.3f alpha:%.2f]", white, alpha) | ||||
| 			: String(format: "[UIColor colorWithRed:%.3f green:%.3f blue:%.3f alpha:%.2f]", red, green, blue, alpha) | ||||
| 	} | ||||
|  | ||||
| 	var swiftString: String { | ||||
| 		red == green && green == blue | ||||
| 			? String(format: "UIColor(white: %.3f, alpha: %.3f", white, alpha) | ||||
| 			// swiftlint:disable:next color_init | ||||
| 			: String(format: "UIColor(red: %.3f, green: %.3f, blue: %.3f, alpha: %.2f)", red, green, blue, alpha) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| extension Color { | ||||
| 	struct Component { | ||||
| 		let keyPath: WritableKeyPath<Color, CGFloat> | ||||
| 		let limit: CGFloat | ||||
| 		let title: String | ||||
| 		private let sliderTintColorForColor: (Color) -> [Color] | ||||
|  | ||||
| 		init( | ||||
| 			keyPath: WritableKeyPath<Color, CGFloat>, | ||||
| 			limit: CGFloat, | ||||
| 			title: String, | ||||
| 			sliderTintColorForColor: @escaping (Color) -> [Color] | ||||
| 		) { | ||||
| 			self.keyPath = keyPath | ||||
| 			self.limit = limit | ||||
| 			self.title = title | ||||
| 			self.sliderTintColorForColor = sliderTintColorForColor | ||||
| 		} | ||||
|  | ||||
| 		init( | ||||
| 			keyPath: WritableKeyPath<Color, CGFloat>, | ||||
| 			limit: CGFloat, | ||||
| 			title: String, | ||||
| 			sliderTint: [Color] | ||||
| 		) { | ||||
| 			self.keyPath = keyPath | ||||
| 			self.limit = limit | ||||
| 			self.title = title | ||||
| 			self.sliderTintColorForColor = { _ in sliderTint } | ||||
| 		} | ||||
|  | ||||
| 		func sliderTintColor(for color: Color) -> [Color] { | ||||
| 			sliderTintColorForColor(color) | ||||
| 		} | ||||
|  | ||||
| 		static let red: Component = .init(keyPath: \.red, limit: 255, title: "Red") { color in | ||||
| 			[ | ||||
| 				Color(red: 0, green: color.green, blue: color.blue, alpha: 1), | ||||
| 				Color(red: 1, green: color.green, blue: color.blue, alpha: 1) | ||||
| 			] | ||||
| 		} | ||||
|  | ||||
| 		static let green: Component = .init(keyPath: \.green, limit: 255, title: "Green") { color in | ||||
| 			[ | ||||
| 				Color(red: color.red, green: 0, blue: color.blue, alpha: 1), | ||||
| 				Color(red: color.red, green: 1, blue: color.blue, alpha: 1) | ||||
| 			] | ||||
| 		} | ||||
|  | ||||
| 		static let blue: Component = .init(keyPath: \.blue, limit: 255, title: "Blue") { color in | ||||
| 			[ | ||||
| 				Color(red: color.red, green: color.green, blue: 0, alpha: 1), | ||||
| 				Color(red: color.red, green: color.green, blue: 1, alpha: 1) | ||||
| 			] | ||||
| 		} | ||||
|  | ||||
| 		static let hue: Component = .init(keyPath: \.hue, limit: 360, title: "Hue") { color in | ||||
| 			Array(0...8).map { Color(hue: CGFloat($0) * 45 / 360, saturation: color.saturation, brightness: color.brightness, alpha: 1) } | ||||
| 		} | ||||
|  | ||||
| 		static let saturation: Component = .init(keyPath: \.saturation, limit: 100, title: "Satur.") { color in | ||||
| 			[ | ||||
| 				.white, | ||||
| 				Color(hue: color.hue, saturation: 1, brightness: color.brightness, alpha: 1) | ||||
| 			] | ||||
| 		} | ||||
|  | ||||
| 		static let brightness: Component = .init(keyPath: \.brightness, limit: 100, title: "Bright") { color in | ||||
| 			[ | ||||
| 				.black, | ||||
| 				Color(hue: color.hue, saturation: color.saturation, brightness: 1, alpha: 1) | ||||
| 			] | ||||
| 		} | ||||
|  | ||||
| 		static let hslSaturation: Component = .init(keyPath: \.hslSaturation, limit: 100, title: "Satur.") { color in | ||||
| 			[ | ||||
| 				Color(hue: color.hue, saturation: 0, lightness: color.lightness, alpha: 1), | ||||
| 				Color(hue: color.hue, saturation: 1, lightness: color.lightness, alpha: 1) | ||||
| 			] | ||||
| 		} | ||||
|  | ||||
| 		static let lightness: Component = .init(keyPath: \.lightness, limit: 100, title: "Light") { color in | ||||
| 			[ | ||||
| 				.black, | ||||
| 				Color(hue: color.hue, saturation: color.hslSaturation, lightness: 0.5, alpha: 1), | ||||
| 				.white | ||||
| 			] | ||||
| 		} | ||||
|  | ||||
| 		static let white: Component = .init(keyPath: \.white, limit: 255, title: "White") { _ in | ||||
| 			[ | ||||
| 				.black, | ||||
| 				.white | ||||
| 			] | ||||
| 		} | ||||
|  | ||||
| 		static let alpha: Component = .init(keyPath: \.alpha, limit: 100, title: "Alpha") { color in | ||||
| 			[ | ||||
| 				Color(red: color.red, green: color.green, blue: color.blue, alpha: 0), | ||||
| 				Color(red: color.red, green: color.green, blue: color.blue, alpha: 1) | ||||
| 			] | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,218 @@ | ||||
| // | ||||
| //  ColorPickerAccessibilityViewController.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 8/5/2022. | ||||
| //  Copyright © 2022 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class ColorPickerAccessibilityViewController: ColorPickerTabViewController { | ||||
|  | ||||
| 	static let title = "Contrast Checker" | ||||
| 	static let imageName = "circle.righthalf.fill" | ||||
|  | ||||
| 	private static let percentFormatter: NumberFormatter = { | ||||
| 		let formatter = NumberFormatter() | ||||
| 		formatter.numberStyle = .percent | ||||
| 		return formatter | ||||
| 	}() | ||||
|  | ||||
| 	private var backgroundMode: AccessibilityContrastSelector.Mode = .white { | ||||
| 		didSet { colorDidChange() } | ||||
| 	} | ||||
| 	private var foregroundMode: AccessibilityContrastSelector.Mode = .color { | ||||
| 		didSet { colorDidChange() } | ||||
| 	} | ||||
|  | ||||
| 	private var scrollView: UIScrollView! | ||||
|  | ||||
| 	private var demoContainerView: UIView! | ||||
| 	private var demoLabels: [UIView]! | ||||
|  | ||||
| 	private var contrastStackView: UIStackView! | ||||
| 	private var contrastRatioLabel: UILabel! | ||||
| 	private var aaComplianceLabel: AccessibilityComplianceLabel! | ||||
| 	private var aaaComplianceLabel: AccessibilityComplianceLabel! | ||||
|  | ||||
| 	private var backgroundSelector: AccessibilityContrastSelector! | ||||
| 	private var foregroundSelector: AccessibilityContrastSelector! | ||||
|  | ||||
| 	override func viewDidLoad() { | ||||
| 		super.viewDidLoad() | ||||
|  | ||||
| 		// Catalyst in iPad UI mode scales all UI down to 77%, so we cancel out the scaling (as well as | ||||
| 		// we possibly can, given scaling down is throwing away quality) for the demo labels. | ||||
| 		let scaleFactor: CGFloat = isCatalystPad ? 1 / 0.77 : 1 | ||||
|  | ||||
| 		let demoTitleLabel = UILabel() | ||||
| 		demoTitleLabel.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		demoTitleLabel.font = .systemFont(ofSize: 18 * scaleFactor, weight: .semibold) | ||||
| 		demoTitleLabel.text = "Size 18 • Contrast Checker" | ||||
|  | ||||
| 		let demoImageView = UIImageView(image: Assets.systemImage(named: "sparkles", font: demoTitleLabel.font, scale: .small)) | ||||
| 		demoImageView.translatesAutoresizingMaskIntoConstraints = false | ||||
|  | ||||
| 		let titleStackView = UIStackView(arrangedSubviews: [demoImageView, demoTitleLabel]) | ||||
| 		titleStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		titleStackView.spacing = UIFloat(6) | ||||
| 		titleStackView.alignment = .center | ||||
|  | ||||
| 		let demoSubtitleLabel = UILabel() | ||||
| 		demoSubtitleLabel.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		demoSubtitleLabel.font = .systemFont(ofSize: 14 * scaleFactor, weight: .medium) | ||||
| 		demoSubtitleLabel.text = "Size 14 • Contrast ratios are a measure of how easily text and images can be read, especially by people with lower vision." | ||||
| 		demoSubtitleLabel.numberOfLines = 0 | ||||
|  | ||||
| 		let demoTextLabel = TextViewLabel() | ||||
| 		demoTextLabel.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		demoTextLabel.linkTextAttributes = [ | ||||
| 			.underlineStyle: NSUnderlineStyle.single.rawValue | ||||
| 		] | ||||
| 		let explainerText = "Size 12 • Learn more about minimum (AA) and enhanced (AAA) contrast." | ||||
| 		let attributedString = NSMutableAttributedString(string: explainerText, | ||||
| 																										 attributes: [ | ||||
| 																											.font: UIFont.systemFont(ofSize: 12 * scaleFactor, weight: .regular) | ||||
| 																										 ]) | ||||
| 		attributedString.addAttribute(.link, | ||||
| 																	value: URL(string: "https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum")!, | ||||
| 																	range: (attributedString.string as NSString).range(of: "minimum (AA)")) | ||||
| 		attributedString.addAttribute(.link, | ||||
| 																	value: URL(string: "https://www.w3.org/WAI/WCAG21/Understanding/contrast-enhanced")!, | ||||
| 																	range: (attributedString.string as NSString).range(of: "enhanced (AAA)")) | ||||
| 		demoTextLabel.attributedText = attributedString | ||||
|  | ||||
| 		demoLabels = [demoTitleLabel, demoSubtitleLabel, demoTextLabel] | ||||
|  | ||||
| 		let demoStackView = UIStackView(arrangedSubviews: [titleStackView, demoSubtitleLabel, demoTextLabel]) | ||||
| 		demoStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		demoStackView.axis = .vertical | ||||
| 		demoStackView.alignment = .leading | ||||
| 		demoStackView.spacing = UIFloat(8) | ||||
|  | ||||
| 		demoContainerView = UIView() | ||||
| 		demoContainerView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		demoContainerView.layer.cornerRadius = 12 | ||||
| 		if #available(iOS 13, *) { | ||||
| 			demoContainerView.layer.cornerCurve = .continuous | ||||
| 		} | ||||
| 		demoContainerView.addSubview(demoStackView) | ||||
|  | ||||
| 		contrastRatioLabel = UILabel() | ||||
| 		contrastRatioLabel.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		contrastRatioLabel.font = .systemFont(ofSize: UIFloat(16), weight: .medium) | ||||
|  | ||||
| 		aaComplianceLabel = AccessibilityComplianceLabel(text: "AA") | ||||
| 		aaaComplianceLabel = AccessibilityComplianceLabel(text: "AAA") | ||||
|  | ||||
| 		let complianceStackView = UIStackView(arrangedSubviews: [UIView(), aaComplianceLabel, aaaComplianceLabel]) | ||||
| 		complianceStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		complianceStackView.spacing = UIFloat(12) | ||||
|  | ||||
| 		contrastStackView = UIStackView(arrangedSubviews: [contrastRatioLabel, complianceStackView]) | ||||
| 		contrastStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		contrastStackView.spacing = UIFloat(8) | ||||
|  | ||||
| 		backgroundSelector = AccessibilityContrastSelector(text: "Background", value: backgroundMode) | ||||
| 		backgroundSelector.handleChange = { self.backgroundMode = $0 } | ||||
|  | ||||
| 		foregroundSelector = AccessibilityContrastSelector(text: "Foreground", value: foregroundMode) | ||||
| 		foregroundSelector.handleChange = { self.foregroundMode = $0 } | ||||
|  | ||||
| 		scrollView = UIScrollView() | ||||
| 		scrollView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		scrollView.alwaysBounceVertical = false | ||||
| 		view.addSubview(scrollView) | ||||
|  | ||||
| 		let rootStackView = UIStackView(arrangedSubviews: [demoContainerView, UIView(), contrastStackView, backgroundSelector, foregroundSelector, UIView()]) | ||||
| 		rootStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		rootStackView.axis = .vertical | ||||
| 		rootStackView.alignment = .fill | ||||
| 		rootStackView.distribution = .fill | ||||
| 		rootStackView.spacing = UIFloat(10) | ||||
| 		scrollView.addSubview(rootStackView) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			scrollView.topAnchor.constraint(equalTo: view.topAnchor), | ||||
| 			scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), | ||||
| 			scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), | ||||
| 			scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), | ||||
|  | ||||
| 			rootStackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor, constant: UIFloat(15)), | ||||
| 			rootStackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor, constant: UIFloat(-15)), | ||||
| 			rootStackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor, constant: UIFloat(15)), | ||||
| 			rootStackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor, constant: UIFloat(-15)), | ||||
| 			rootStackView.heightAnchor.constraint(equalTo: scrollView.contentLayoutGuide.heightAnchor, constant: UIFloat(-30)), | ||||
| 			scrollView.contentLayoutGuide.widthAnchor.constraint(equalTo: scrollView.widthAnchor), | ||||
| 			scrollView.contentLayoutGuide.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.heightAnchor), | ||||
|  | ||||
| 			demoStackView.topAnchor.constraint(equalTo: demoContainerView.topAnchor, constant: UIFloat(16)), | ||||
| 			demoStackView.bottomAnchor.constraint(equalTo: demoContainerView.bottomAnchor, constant: UIFloat(-17)), | ||||
| 			demoStackView.leadingAnchor.constraint(equalTo: demoContainerView.leadingAnchor, constant: UIFloat(20)), | ||||
| 			demoStackView.trailingAnchor.constraint(equalTo: demoContainerView.trailingAnchor, constant: UIFloat(-20)), | ||||
|  | ||||
| 			contrastStackView.heightAnchor.constraint(greaterThanOrEqualTo: backgroundSelector.heightAnchor), | ||||
| 			contrastStackView.heightAnchor.constraint(greaterThanOrEqualTo: foregroundSelector.heightAnchor) | ||||
| 		]) | ||||
|  | ||||
| 		colorDidChange() | ||||
| 	} | ||||
|  | ||||
| 	override func viewDidAppear(_ animated: Bool) { | ||||
| 		super.viewDidAppear(animated) | ||||
|  | ||||
| 		scrollView.flashScrollIndicators() | ||||
| 	} | ||||
|  | ||||
| 	override func viewWillLayoutSubviews() { | ||||
| 		super.viewWillLayoutSubviews() | ||||
|  | ||||
| 		contrastStackView.axis = view.frame.size.width > UIFloat(300) ? .horizontal : .vertical | ||||
| 	} | ||||
|  | ||||
| 	override func colorDidChange() { | ||||
| 		let backgroundColor = backgroundMode.color(withColor: color) | ||||
| 		let foregroundColor = foregroundMode.color(withColor: color) | ||||
|  | ||||
| 		if backgroundColor == foregroundColor { | ||||
| 			// Change one or the other to not be identical | ||||
| 			if backgroundMode == foregroundMode { | ||||
| 				switch backgroundMode { | ||||
| 				case .black, .white: foregroundMode = .color | ||||
| 				default:             foregroundMode = .white | ||||
| 				} | ||||
| 			} else { | ||||
| 				if foregroundMode == .color { | ||||
| 					backgroundMode = backgroundColor == .white ? .black : .white | ||||
| 				} else if backgroundMode == .color { | ||||
| 					foregroundMode = foregroundColor == .white ? .black : .white | ||||
| 				} | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		backgroundSelector.value = backgroundMode | ||||
| 		foregroundSelector.value = foregroundMode | ||||
|  | ||||
| 		demoContainerView.backgroundColor = backgroundColor.uiColor | ||||
| 		demoContainerView.tintColor = foregroundColor.uiColor | ||||
| 		for label in demoLabels { | ||||
| 			if let label = label as? UILabel { | ||||
| 				label.textColor = foregroundColor.uiColor | ||||
| 			} else if let label = label as? UITextView, | ||||
| 								let attributedString = label.attributedText.mutableCopy() as? NSMutableAttributedString { | ||||
| 				attributedString.addAttribute(.foregroundColor, | ||||
| 																			value: foregroundColor.uiColor, | ||||
| 																			range: NSRange(location: 0, length: attributedString.string.count)) | ||||
| 				label.attributedText = attributedString | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		let contrastRatio = foregroundColor.perceivedBrightness(onBackgroundColor: backgroundColor) | ||||
| 		contrastRatioLabel.text = "Contrast: \(String(format: "%.2f", contrastRatio)) (\(Self.percentFormatter.string(for: contrastRatio / 21)!))" | ||||
| 		aaComplianceLabel.isCompliant = contrastRatio > 4.5 | ||||
| 		aaaComplianceLabel.isCompliant = contrastRatio > 7 | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										91
									
								
								Tweaks/Alderis/Alderis/ColorPickerConfiguration.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | ||||
| // | ||||
| //  ColorPickerConfiguration.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 10/5/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| /// An enumeration of the tabs `ColorPickerViewController` features. Use these enumeration values to | ||||
| /// set tab-related settings on `ColorPickerConfiguration`. | ||||
| @objc(HBColorPickerTab) | ||||
| public enum ColorPickerTab: Int, CaseIterable { | ||||
| 	/// Tab 1: A grid of 9 variations of 11 colours, and a grayscale ramp. The first and default tab. | ||||
| 	case swatch = 0 | ||||
|  | ||||
| 	/// Tab 2: A color wheel displaying every possible hue and saturation combination. The user can | ||||
| 	/// additionally adjust the brightness of the colour using a slider. | ||||
| 	case map = 1 | ||||
|  | ||||
| 	/// Tab 3: A set of sliders for red, green, and blue color values, which can be switched to hue, | ||||
| 	/// saturation, and brightness. The user can additionally copy or enter a color value expressed | ||||
| 	/// using a CSS-style hexadecimal string, and adjust alpha transparency. | ||||
| 	case sliders = 2 | ||||
|  | ||||
| 	/// Tab 4: A tab that allows the user to test various configurations of the color, and its | ||||
| 	/// conformance to WCAG color contrast. | ||||
| 	case accessibility = 3 | ||||
| } | ||||
|  | ||||
| /// ColorPickerConfiguration is used to configure an instance of `ColorPickerViewController`. | ||||
| @objc(HBColorPickerConfiguration) | ||||
| open class ColorPickerConfiguration: NSObject { | ||||
|  | ||||
| 	/// Initialise a configuration object with the required color property configured. | ||||
| 	@objc public init(color: UIColor) { | ||||
| 		self.color = color | ||||
| 		super.init() | ||||
| 	} | ||||
|  | ||||
| 	/// The initial color to use when launching the color picker. Required. If you don’t have a value | ||||
| 	/// set yet, provide a sensible default. | ||||
| 	@objc open var color: UIColor | ||||
|  | ||||
| 	/// Whether to allow the user to set an alpha transparency value on the color. This controls the | ||||
| 	/// visibility of an Alpha slider on the Sliders tab. When set to `false`, alpha values provided | ||||
| 	/// via the `color` property, or by the user when entering a hexadecimal value on the Sliders tab, | ||||
| 	/// will be discarded. | ||||
| 	@objc open var supportsAlpha = true | ||||
|  | ||||
| 	/// The title to display at the top of the popup. If set to `nil`, no title will be displayed. The | ||||
| 	/// default is `nil`. | ||||
| 	@objc open var title: String? | ||||
|  | ||||
| 	/// The initial tab to select when the color picker is presented. The default is | ||||
| 	/// `ColorPickerTab.swatch`. | ||||
| 	/// | ||||
| 	/// This value must be found in `visibleTabs`. | ||||
| 	/// | ||||
| 	/// - see: `visibleTabs` | ||||
| 	@objc open var initialTab = ColorPickerTab.swatch | ||||
|  | ||||
| 	/// The tabs the user can select and switch between at the top of the window, if tabs are enabled | ||||
| 	/// by `showTabs`. | ||||
| 	/// | ||||
| 	/// - see: `initialTab` | ||||
| 	@nonobjc open var visibleTabs: [ColorPickerTab] = [.swatch, .map, .sliders, .accessibility] | ||||
|  | ||||
| 	/// Maps `visibleTabs` to Objective-C due to Swift limitations. This is an implementation detail. | ||||
| 	/// Ignore this and use `visibleTabs` per usual. | ||||
| 	@objc(visibleTabs) | ||||
| 	open var _visibleTabsObjC: [ColorPickerTab.RawValue] { | ||||
| 		get { visibleTabs.map(\.rawValue) } | ||||
| 		set { visibleTabs = newValue.map { ColorPickerTab(rawValue: $0)! } } | ||||
| 	} | ||||
|  | ||||
| 	/// Whether to display the tab selection at the top of the popup. The default is `true`. When set | ||||
| 	/// to `false`, the user will only be able to access the tab specified in initialTab. | ||||
| 	@objc open var showTabs = true | ||||
|  | ||||
| 	/// When the Smart Invert accessibility feature is enabled, Alderis instructs the system to not | ||||
| 	/// invert most of its user interface. This ensures the user can make a more accurate color | ||||
| 	/// selection. If this behavior is not desired, you can disable it here. | ||||
| 	@objc open var overrideSmartInvert = true | ||||
|  | ||||
| 	/// Whether the user can end a drag interaction by dropping on the color picker window, allowing | ||||
| 	/// them to drag a color from a supporting app. The default is `true`. | ||||
| 	@objc open var isDropInteractionEnabled = true | ||||
|  | ||||
| } | ||||
							
								
								
									
										51
									
								
								Tweaks/Alderis/Alderis/ColorPickerDelegate.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,51 @@ | ||||
| // | ||||
| //  ColorPickerDelegate.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 16/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| /// Use `ColorPickerDelegate` to handle the user’s response to `ColorPickerViewController`. | ||||
| @objc(HBColorPickerDelegate) | ||||
| public protocol ColorPickerDelegate: NSObjectProtocol { | ||||
|  | ||||
| 	/// Informs the delegate that the user has selected a color in the color picker. Optional. | ||||
| 	/// | ||||
| 	/// Use this to update your user interface with the new color value, if suitable to your use case. | ||||
| 	/// | ||||
| 	/// You should, at minimum, implement either this method or `colorPicker(_:didAccept:)`. If you | ||||
| 	/// don’t intend to implement this method, it is expected that you implement | ||||
| 	/// `colorPicker(_:didAccept:)`. If you implement this method and the user selects Cancel, this | ||||
| 	/// method will be called with the initial color passed in via `ColorPickerConfiguration.color` to | ||||
| 	/// undo the selection. | ||||
| 	/// | ||||
| 	/// - parameter colorPicker: The `ColorPickerViewController` instance that triggered the action. | ||||
| 	/// - parameter color: The `UIColor` selection the user made. | ||||
| 	/// - see: `colorPicker(_:didAccept:)` | ||||
| 	@objc(colorPicker:didSelectColor:) | ||||
| 	optional func colorPicker(_ colorPicker: ColorPickerViewController, didSelect color: UIColor) | ||||
|  | ||||
| 	/// Informs the delegate that the user has dismissed the color picker with a positive response, | ||||
| 	/// having selected the selected color. Optional. | ||||
| 	/// | ||||
| 	/// You should, at minimum, implement either this method or `colorPicker(_:didSelect:)`. | ||||
| 	/// | ||||
| 	/// - parameter colorPicker: The `ColorPickerViewController` instance that triggered the action. | ||||
| 	/// - parameter color: The `UIColor` selection the user made. | ||||
| 	/// - see: `colorPicker(_:didSelect:)` | ||||
| 	@objc(colorPicker:didAcceptColor:) | ||||
| 	optional func colorPicker(_ colorPicker: ColorPickerViewController, didAccept color: UIColor) | ||||
|  | ||||
| 	/// Informs the delegate that the user has dismissed the color picker with a negative response. | ||||
| 	/// Optional. | ||||
| 	/// | ||||
| 	/// You usually do not need to handle this condition. | ||||
| 	/// | ||||
| 	/// - parameter colorPicker: The `ColorPickerViewController` instance that triggered the action. | ||||
| 	@objc(colorPickerDidCancel:) | ||||
| 	optional func colorPickerDidCancel(_ colorPicker: ColorPickerViewController) | ||||
|  | ||||
| } | ||||
							
								
								
									
										445
									
								
								Tweaks/Alderis/Alderis/ColorPickerInnerViewController.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,445 @@ | ||||
| // | ||||
| //  ColorPickerInnerViewController.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 12/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal extension ColorPickerTab { | ||||
| 	var tabClass: ColorPickerTabViewController.Type { | ||||
| 		switch self { | ||||
| 		case .swatch:		return ColorPickerSwatchViewController.self | ||||
| 		case .map:			return ColorPickerMapViewController.self | ||||
| 		case .sliders:	return ColorPickerSlidersViewController.self | ||||
| 		case .accessibility: return ColorPickerAccessibilityViewController.self | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var index: Int { Self.allCases.firstIndex(of: self)! } | ||||
| } | ||||
|  | ||||
| internal class ColorPickerInnerViewController: UIViewController { | ||||
|  | ||||
| 	weak var delegate: ColorPickerDelegate? | ||||
| 	let configuration: ColorPickerConfiguration | ||||
| 	var color: Color | ||||
|  | ||||
| 	var tab: ColorPickerTab { | ||||
| 		get { configuration.visibleTabs[currentTab] } | ||||
| 		set { currentTab = configuration.visibleTabs.firstIndex(of: newValue) ?? 0 } | ||||
| 	} | ||||
|  | ||||
| 	var compatibilityMode = false | ||||
|  | ||||
| 	private var colorPicker: ColorPickerViewController { | ||||
| 		// swiftlint:disable:next force_cast | ||||
| 		parent as! ColorPickerViewController | ||||
| 	} | ||||
|  | ||||
| 	init(delegate: ColorPickerDelegate?, configuration: ColorPickerConfiguration) { | ||||
| 		self.delegate = delegate | ||||
| 		self.configuration = configuration | ||||
| 		color = Color(uiColor: configuration.color) | ||||
| 		super.init(nibName: nil, bundle: nil) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	private var currentTab = 0 { | ||||
| 		didSet { tabDidChange(oldValue: oldValue) } | ||||
| 	} | ||||
|  | ||||
| 	func setColor(_ color: Color, withSource source: ColorPickerTabViewControllerBase? = nil) { | ||||
| 		self.color = color | ||||
| 		colorDidChange(withSource: source) | ||||
| 	} | ||||
|  | ||||
| 	private var pageViewController: UIPageViewController! | ||||
| 	private var tabs = [ColorPickerTabViewController]() | ||||
| 	private var tabsView: UISegmentedControl! | ||||
| 	private var titleLabel: UILabel! | ||||
| 	private var cancelButton: DialogButton? | ||||
| 	private var saveButton: DialogButton? | ||||
| 	private var tabsBackgroundView: UIView! | ||||
| 	private var buttonsBackgroundView: UIView? | ||||
| 	private var heightConstraint: NSLayoutConstraint! | ||||
| 	private var backgroundView: UIView! | ||||
|  | ||||
| 	override func viewDidLoad() { | ||||
| 		super.viewDidLoad() | ||||
|  | ||||
| 		for tabType in configuration.visibleTabs { | ||||
| 			let tab = tabType.tabClass.init(tabDelegate: self, configuration: configuration, color: color) | ||||
| 			// Force the view to be initialised | ||||
| 			tab.loadViewIfNeeded() | ||||
| 			tabs.append(tab) | ||||
| 		} | ||||
|  | ||||
| 		if configuration.isDropInteractionEnabled { | ||||
| 			view.addInteraction(UIDropInteraction(delegate: self)) | ||||
| 		} | ||||
|  | ||||
| 		backgroundView = UIView() | ||||
| 		backgroundView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		backgroundView.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 		view.addSubview(backgroundView) | ||||
|  | ||||
| 		let tabsCheckerboardView = UIView() | ||||
| 		tabsCheckerboardView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		tabsCheckerboardView.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 		tabsCheckerboardView.backgroundColor = Assets.checkerboardPatternColor | ||||
| 		view.addSubview(tabsCheckerboardView) | ||||
|  | ||||
| 		tabsBackgroundView = UIView() | ||||
| 		tabsBackgroundView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		tabsBackgroundView.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 		view.addSubview(tabsBackgroundView) | ||||
|  | ||||
| 		let topSeparatorView = SeparatorView(direction: .horizontal) | ||||
| 		topSeparatorView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		tabsBackgroundView.addSubview(topSeparatorView) | ||||
|  | ||||
| 		let titleView = UIView() | ||||
| 		titleView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		titleView.isHidden = configuration.title == nil || configuration.title!.isEmpty | ||||
|  | ||||
| 		titleLabel = UILabel() | ||||
| 		titleLabel.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		titleLabel.textAlignment = .center | ||||
| 		titleLabel.font = .systemFont(ofSize: UIFloat(17), weight: .semibold) | ||||
| 		titleLabel.text = configuration.title | ||||
| 		titleView.addSubview(titleLabel) | ||||
|  | ||||
| 		let tabsContainerView = UIView() | ||||
| 		tabsContainerView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		tabsContainerView.isHidden = !configuration.showTabs | ||||
|  | ||||
| 		tabsView = UISegmentedControl() | ||||
| 		tabsView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		tabsView.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 		tabsView.addTarget(self, action: #selector(segmentControlChanged(_:)), for: .valueChanged) | ||||
| 		tabsContainerView.addSubview(tabsView) | ||||
|  | ||||
| 		if #available(iOS 13, *) { | ||||
| 			tabsView.selectedSegmentTintColor = UIColor.white.withAlphaComponent(0.35) | ||||
| 			if isCatalystMac { | ||||
| 				tabsView.setTitleTextAttributes([ .foregroundColor: Assets.macTabBarSelectionColor ], for: .highlighted) | ||||
| 				tabsView.setTitleTextAttributes([ .foregroundColor: Assets.macTabBarSelectionColor ], for: .selected) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for (i, tab) in tabs.enumerated() { | ||||
| 			let tabClass = type(of: tab) | ||||
| 			#if swift(>=5.3) | ||||
| 			if #available(iOS 14, *) { | ||||
| 				tabsView.insertSegment(action: UIAction(title: tabClass.title, | ||||
| 																								image: tabClass.image, | ||||
| 																								handler: { _ in }), | ||||
| 															 at: i, | ||||
| 															 animated: false) | ||||
| 			} else { | ||||
| 				tabsView.insertSegment(with: tabClass.image, at: i, animated: false) | ||||
| 			} | ||||
| 			#else | ||||
| 			tabsView.insertSegment(with: tabClass.image, at: i, animated: false) | ||||
| 			#endif | ||||
| 		} | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			tabsView.centerXAnchor.constraint(equalTo: tabsContainerView.centerXAnchor), | ||||
| 			tabsView.centerYAnchor.constraint(equalTo: tabsContainerView.centerYAnchor), | ||||
| 			tabsView.leadingAnchor.constraint(greaterThanOrEqualTo: tabsContainerView.leadingAnchor, constant: 4), | ||||
| 			tabsView.trailingAnchor.constraint(lessThanOrEqualTo: tabsContainerView.trailingAnchor, constant: -4) | ||||
| 		]) | ||||
|  | ||||
| 		if #available(iOS 13, *) { | ||||
| 		} else { | ||||
| 			NSLayoutConstraint.activate([ | ||||
| 				tabsView.heightAnchor.constraint(equalToConstant: 32) | ||||
| 			]) | ||||
| 			for i in 0..<tabsView.numberOfSegments { | ||||
| 				tabsView.setWidth(40, forSegmentAt: i) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		let pageViewContainer = UIView() | ||||
| 		pageViewContainer.translatesAutoresizingMaskIntoConstraints = false | ||||
|  | ||||
| 		pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:]) | ||||
| 		pageViewController.view.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		pageViewController.willMove(toParent: self) | ||||
| 		addChild(pageViewController) | ||||
| 		pageViewContainer.addSubview(pageViewController.view) | ||||
|  | ||||
| 		let mainStackView = UIStackView(arrangedSubviews: [titleView, tabsContainerView, pageViewContainer]) | ||||
| 		mainStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		mainStackView.axis = .vertical | ||||
| 		mainStackView.alignment = .fill | ||||
| 		mainStackView.distribution = .fill | ||||
| 		view.addSubview(mainStackView) | ||||
|  | ||||
| 		heightConstraint = pageViewContainer.heightAnchor.constraint(equalToConstant: 0) | ||||
|  | ||||
| 		let barHeight: CGFloat = UIFloat(48) | ||||
| 		let topHeight = (titleView.isHidden ? 0 : barHeight) + (tabsContainerView.isHidden ? 0 : barHeight) | ||||
| 		let titleLabelTopOffset: CGFloat = tabsContainerView.isHidden ? 0 : 3 | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			backgroundView.topAnchor.constraint(equalTo: view.topAnchor), | ||||
| 			backgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor), | ||||
| 			backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor), | ||||
| 			backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor), | ||||
|  | ||||
| 			mainStackView.topAnchor.constraint(equalTo: view.topAnchor), | ||||
| 			mainStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor), | ||||
| 			mainStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor), | ||||
| 			mainStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor), | ||||
|  | ||||
| 			titleView.heightAnchor.constraint(equalToConstant: barHeight), | ||||
| 			tabsContainerView.heightAnchor.constraint(equalToConstant: barHeight), | ||||
|  | ||||
| 			titleLabel.topAnchor.constraint(equalTo: titleView.topAnchor, constant: titleLabelTopOffset), | ||||
| 			titleLabel.bottomAnchor.constraint(equalTo: titleView.bottomAnchor), | ||||
| 			titleLabel.leadingAnchor.constraint(equalTo: titleView.leadingAnchor, constant: 4), | ||||
| 			titleLabel.trailingAnchor.constraint(equalTo: titleView.trailingAnchor, constant: -4), | ||||
|  | ||||
| 			tabsBackgroundView.topAnchor.constraint(equalTo: view.topAnchor), | ||||
| 			tabsBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor), | ||||
| 			tabsBackgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor), | ||||
| 			tabsBackgroundView.heightAnchor.constraint(equalToConstant: topHeight), | ||||
|  | ||||
| 			tabsCheckerboardView.topAnchor.constraint(equalTo: tabsBackgroundView.topAnchor), | ||||
| 			tabsCheckerboardView.bottomAnchor.constraint(equalTo: tabsBackgroundView.bottomAnchor), | ||||
| 			tabsCheckerboardView.leadingAnchor.constraint(equalTo: tabsBackgroundView.leadingAnchor), | ||||
| 			tabsCheckerboardView.trailingAnchor.constraint(equalTo: tabsBackgroundView.trailingAnchor), | ||||
|  | ||||
| 			topSeparatorView.leadingAnchor.constraint(equalTo: tabsBackgroundView.leadingAnchor), | ||||
| 			topSeparatorView.trailingAnchor.constraint(equalTo: tabsBackgroundView.trailingAnchor), | ||||
| 			topSeparatorView.bottomAnchor.constraint(equalTo: tabsBackgroundView.bottomAnchor), | ||||
|  | ||||
| 			pageViewController.view.topAnchor.constraint(equalTo: pageViewContainer.topAnchor), | ||||
| 			pageViewController.view.bottomAnchor.constraint(equalTo: pageViewContainer.bottomAnchor), | ||||
| 			pageViewController.view.leadingAnchor.constraint(equalTo: pageViewContainer.leadingAnchor), | ||||
| 			pageViewController.view.trailingAnchor.constraint(equalTo: pageViewContainer.trailingAnchor), | ||||
|  | ||||
| 			heightConstraint | ||||
| 		]) | ||||
|  | ||||
| 		if !isCatalystMac { | ||||
| 			let buttonsCheckerboardView = UIView() | ||||
| 			buttonsCheckerboardView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			buttonsCheckerboardView.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 			buttonsCheckerboardView.backgroundColor = Assets.checkerboardPatternColor | ||||
| 			view.insertSubview(buttonsCheckerboardView, aboveSubview: tabsBackgroundView) | ||||
|  | ||||
| 			let buttonsBackgroundView = UIView() | ||||
| 			buttonsBackgroundView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			buttonsBackgroundView.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 			view.insertSubview(buttonsBackgroundView, aboveSubview: buttonsCheckerboardView) | ||||
| 			self.buttonsBackgroundView = buttonsBackgroundView | ||||
|  | ||||
| 			let bottomSeparatorView = SeparatorView(direction: .horizontal) | ||||
| 			bottomSeparatorView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			buttonsBackgroundView.addSubview(bottomSeparatorView) | ||||
|  | ||||
| 			let cancelButton = DialogButton() | ||||
| 			cancelButton.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			cancelButton.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 			cancelButton.titleLabel!.font = .systemFont(ofSize: 17, weight: .regular) | ||||
| 			cancelButton.setTitle(Assets.uikitLocalize("Cancel"), for: .normal) | ||||
| 			cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside) | ||||
| 			self.cancelButton = cancelButton | ||||
|  | ||||
| 			let saveButton = DialogButton() | ||||
| 			saveButton.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			saveButton.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 			saveButton.titleLabel!.font = .systemFont(ofSize: 17, weight: .semibold) | ||||
| 			saveButton.setTitle(Assets.uikitLocalize("Done"), for: .normal) | ||||
| 			saveButton.addTarget(self, action: #selector(saveTapped), for: .touchUpInside) | ||||
| 			self.saveButton = saveButton | ||||
|  | ||||
| 			let buttonSeparatorView = SeparatorView(direction: .vertical) | ||||
| 			buttonSeparatorView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			buttonsBackgroundView.addSubview(buttonSeparatorView) | ||||
|  | ||||
| 			let buttonsView = UIStackView(arrangedSubviews: [cancelButton, saveButton]) | ||||
| 			buttonsView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			buttonsView.axis = .horizontal | ||||
| 			buttonsView.alignment = .fill | ||||
| 			mainStackView.addSubview(buttonsView) | ||||
| 			mainStackView.addArrangedSubview(buttonsView) | ||||
|  | ||||
| 			NSLayoutConstraint.activate([ | ||||
| 				buttonsView.heightAnchor.constraint(equalToConstant: barHeight), | ||||
|  | ||||
| 				buttonsBackgroundView.topAnchor.constraint(equalTo: buttonsView.topAnchor), | ||||
| 				buttonsBackgroundView.bottomAnchor.constraint(equalTo: buttonsView.bottomAnchor), | ||||
| 				buttonsBackgroundView.leadingAnchor.constraint(equalTo: buttonsView.leadingAnchor), | ||||
| 				buttonsBackgroundView.trailingAnchor.constraint(equalTo: buttonsView.trailingAnchor), | ||||
|  | ||||
| 				buttonSeparatorView.heightAnchor.constraint(equalToConstant: barHeight / 2), | ||||
| 				buttonSeparatorView.centerXAnchor.constraint(equalTo: buttonsBackgroundView.centerXAnchor), | ||||
| 				buttonSeparatorView.centerYAnchor.constraint(equalTo: buttonsBackgroundView.centerYAnchor), | ||||
|  | ||||
| 				buttonsCheckerboardView.topAnchor.constraint(equalTo: buttonsBackgroundView.topAnchor), | ||||
| 				buttonsCheckerboardView.bottomAnchor.constraint(equalTo: buttonsBackgroundView.bottomAnchor), | ||||
| 				buttonsCheckerboardView.leadingAnchor.constraint(equalTo: buttonsBackgroundView.leadingAnchor), | ||||
| 				buttonsCheckerboardView.trailingAnchor.constraint(equalTo: buttonsBackgroundView.trailingAnchor), | ||||
|  | ||||
| 				bottomSeparatorView.leadingAnchor.constraint(equalTo: buttonsBackgroundView.leadingAnchor), | ||||
| 				bottomSeparatorView.trailingAnchor.constraint(equalTo: buttonsBackgroundView.trailingAnchor), | ||||
| 				bottomSeparatorView.topAnchor.constraint(equalTo: buttonsBackgroundView.topAnchor), | ||||
|  | ||||
| 				cancelButton.widthAnchor.constraint(equalTo: saveButton.widthAnchor) | ||||
| 			]) | ||||
| 		} | ||||
|  | ||||
| 		colorDidChange() | ||||
| 		tab = configuration.initialTab | ||||
| 		tabsView?.selectedSegmentIndex = currentTab | ||||
| 	} | ||||
|  | ||||
| 	override func viewWillLayoutSubviews() { | ||||
| 		super.viewWillLayoutSubviews() | ||||
| 		updateHeightConstraint() | ||||
| 	} | ||||
|  | ||||
| 	override func viewDidLayoutSubviews() { | ||||
| 		super.viewDidLayoutSubviews() | ||||
| 		updateHeightConstraint() | ||||
| 	} | ||||
|  | ||||
| 	override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) { | ||||
| 		super.preferredContentSizeDidChange(forChildContentContainer: container) | ||||
| 		updateHeightConstraint() | ||||
| 	} | ||||
|  | ||||
| 	private func updateHeightConstraint() { | ||||
| 		heightConstraint.constant = (view.frame.size.width / 12) * 10 | ||||
| 		preferredContentSize = view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) | ||||
| 	} | ||||
|  | ||||
| 	@objc private func segmentControlChanged(_ sender: UISegmentedControl) { | ||||
| 		UIView.animate(withDuration: 0.2) { | ||||
| 			self.currentTab = self.tabsView.selectedSegmentIndex | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@objc private func oldTabButtonTapped(_ sender: UIButton) { | ||||
| 		UIView.animate(withDuration: 0.2) { | ||||
| 			self.currentTab = sender.tag | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@objc private func cancelTapped() { | ||||
| 		if !compatibilityMode { | ||||
| 			delegate?.colorPicker?(colorPicker, didSelect: configuration.color) | ||||
| 		} | ||||
| 		delegate?.colorPickerDidCancel?(colorPicker) | ||||
| 		dismiss(animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func saveTapped() { | ||||
| 		if compatibilityMode { | ||||
| 			delegate?.colorPicker?(colorPicker, didSelect: color.uiColor) | ||||
| 		} else { | ||||
| 			delegate?.colorPicker?(colorPicker, didAccept: color.uiColor) | ||||
| 		} | ||||
| 		dismiss(animated: true) | ||||
| 	} | ||||
|  | ||||
| 	private func colorDidChange(withSource source: ColorPickerTabViewControllerBase? = nil) { | ||||
| 		let uiColor = color.uiColor | ||||
| 		if !compatibilityMode { | ||||
| 			delegate?.colorPicker?(colorPicker, didSelect: uiColor) | ||||
| 		} | ||||
|  | ||||
| 		let foregroundColor: UIColor = color.isDark ? .white : .black | ||||
|  | ||||
| 		view.tintColor = uiColor | ||||
| 		tabsBackgroundView.backgroundColor = uiColor | ||||
| 		buttonsBackgroundView?.backgroundColor = uiColor | ||||
| 		titleLabel.textColor = foregroundColor | ||||
| 		cancelButton?.setTitleColor(foregroundColor, for: .normal) | ||||
| 		saveButton?.setTitleColor(foregroundColor, for: .normal) | ||||
| 		cancelButton?.highlightBackgroundColor = foregroundColor.withAlphaComponent(0.25) | ||||
| 		saveButton?.highlightBackgroundColor = foregroundColor.withAlphaComponent(0.25) | ||||
| 		tabsView.setTitleTextAttributes([ .foregroundColor: foregroundColor ], for: .normal) | ||||
|  | ||||
| 		if #available(iOS 13, *) { | ||||
| 		} else { | ||||
| 			tabsView.tintColor = foregroundColor | ||||
| 		} | ||||
|  | ||||
| 		// Even though `shouldBroadcast: false` avoids recursion if we call setColor on the callee tab, | ||||
| 		// doing so on ColorPickerSlidersViewController would reset `hexOptions`, leading to a buggy | ||||
| 		// typing experience in `hexTextField` | ||||
| 		for tab in tabs where tab != source { | ||||
| 			tab.setColor(color, shouldBroadcast: false) | ||||
| 		} | ||||
|  | ||||
| 		backgroundView.backgroundColor = uiColor.withAlphaComponent(color.alpha * 0.2) | ||||
| 	} | ||||
|  | ||||
| 	private func tabDidChange(oldValue: Int) { | ||||
| 		let stuff = { | ||||
| 			let direction: UIPageViewController.NavigationDirection = self.currentTab < oldValue ? .reverse : .forward | ||||
| 			self.pageViewController.setViewControllers([self.tabs[self.currentTab]], direction: direction, animated: !isCatalystMac) | ||||
| 			self.colorDidChange() | ||||
|  | ||||
| 			UIView.animate(withDuration: 0.2) { | ||||
| 				self.view.layoutIfNeeded() | ||||
| 			} | ||||
| 		} | ||||
| 		if isCatalystMac { | ||||
| 			UIView.performWithoutAnimation(stuff) | ||||
| 		} else { | ||||
| 			stuff() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| extension ColorPickerInnerViewController: ColorPickerTabDelegate { | ||||
|  | ||||
| 	func colorPickerTab(_ tab: ColorPickerTabViewControllerBase, didSelect color: Color) { | ||||
| 		setColor(color, withSource: tab) | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| extension ColorPickerInnerViewController: UIDropInteractionDelegate { | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool { | ||||
| 		return session.items.count == 1 && session.canLoadObjects(ofClass: UIColor.self) | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal { | ||||
| 		return UIDropProposal(operation: .copy) | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) { | ||||
| 		session.loadObjects(ofClass: UIColor.self) { items in | ||||
| 			if let color = items.first as? UIColor { | ||||
| 				self.setColor(Color(uiColor: color), withSource: nil) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| extension ColorPickerInnerViewController: UIPopoverPresentationControllerDelegate { | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func presentationControllerWillDismiss(_ presentationController: UIPresentationController) { | ||||
| 		saveTapped() | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										49
									
								
								Tweaks/Alderis/Alderis/ColorPickerMapSlider.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,49 @@ | ||||
| // | ||||
| //  ColorPickerMapSlider.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Kabir Oberai on 23/03/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class ColorPickerMapSlider: ColorPickerComponentSlider { | ||||
|  | ||||
| 	init(minImageName: String, maxImageName: String, component: Color.Component, overrideSmartInvert: Bool) { | ||||
| 		super.init(component: component, overrideSmartInvert: overrideSmartInvert) | ||||
|  | ||||
| 		stackView.alignment = .center | ||||
| 		stackView.spacing = UIFloat(13) | ||||
|  | ||||
| 		let leftImageView = UIImageView(image: Assets.systemImage(named: minImageName)) | ||||
| 		leftImageView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		leftImageView.contentMode = .center | ||||
| 		leftImageView.tintColor = Assets.secondaryLabelColor | ||||
| 		stackView.insertArrangedSubview(leftImageView, at: 0) | ||||
|  | ||||
| 		let rightImageView = UIImageView(image: Assets.systemImage(named: maxImageName)) | ||||
| 		rightImageView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		rightImageView.contentMode = .center | ||||
| 		rightImageView.tintColor = Assets.secondaryLabelColor | ||||
| 		stackView.addArrangedSubview(rightImageView) | ||||
|  | ||||
| 		if #available(iOS 13, *) { | ||||
| 			let symbolConfig = UIImage.SymbolConfiguration(font: .systemFont(ofSize: UIFloat(18), weight: .medium), scale: .medium) | ||||
| 			leftImageView.preferredSymbolConfiguration = symbolConfig | ||||
| 			rightImageView.preferredSymbolConfiguration = symbolConfig | ||||
| 		} | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			leftImageView.widthAnchor.constraint(equalToConstant: UIFloat(22)), | ||||
| 			leftImageView.widthAnchor.constraint(equalTo: rightImageView.widthAnchor), | ||||
| 			leftImageView.heightAnchor.constraint(equalTo: leftImageView.widthAnchor), | ||||
| 			rightImageView.heightAnchor.constraint(equalTo: rightImageView.widthAnchor) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										79
									
								
								Tweaks/Alderis/Alderis/ColorPickerMapViewController.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,79 @@ | ||||
| // | ||||
| //  ColorPickerMapViewController.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 14/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class ColorPickerMapViewController: ColorPickerTabViewController { | ||||
|  | ||||
| 	static let title = "Color Wheel" | ||||
| 	static let imageName = "slider.horizontal.below.rectangle" | ||||
|  | ||||
| 	private var wheelView: ColorPickerWheelView! | ||||
| 	private var sliders = [ColorPickerMapSlider]() | ||||
|  | ||||
| 	override func viewDidLoad() { | ||||
| 		super.viewDidLoad() | ||||
|  | ||||
| 		wheelView = ColorPickerWheelView(color: color) | ||||
| 		wheelView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		wheelView.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 		wheelView.delegate = self | ||||
| 		view.addSubview(wheelView) | ||||
|  | ||||
| 		sliders = [ | ||||
| 			ColorPickerMapSlider( | ||||
| 				minImageName: "sun.min", maxImageName: "sun.max", component: .brightness, | ||||
| 				overrideSmartInvert: configuration.overrideSmartInvert | ||||
| 			) | ||||
| 		] | ||||
|  | ||||
| 		sliders.forEach { | ||||
| 			$0.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			$0.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged) | ||||
| 		} | ||||
|  | ||||
| 		let mainStackView = UIStackView(arrangedSubviews: [wheelView] + sliders) | ||||
| 		mainStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		mainStackView.axis = .vertical | ||||
| 		mainStackView.alignment = .fill | ||||
| 		mainStackView.distribution = .fill | ||||
| 		view.addSubview(mainStackView) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			mainStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: UIFloat(15)), | ||||
| 			mainStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: UIFloat(-15)), | ||||
| 			mainStackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0), | ||||
| 			mainStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: UIFloat(-10)) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	override func viewDidLayoutSubviews() { | ||||
| 		super.viewDidLayoutSubviews() | ||||
| 		colorDidChange() | ||||
| 	} | ||||
|  | ||||
| 	@objc private func sliderChanged(_ slider: ColorPickerMapSlider) { | ||||
| 		var color = self.color | ||||
| 		slider.apply(to: &color) | ||||
| 		self.setColor(color) | ||||
| 	} | ||||
|  | ||||
| 	override func colorDidChange() { | ||||
| 		wheelView.color = color | ||||
| 		sliders.forEach { $0.setColor(color) } | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| extension ColorPickerMapViewController: ColorPickerWheelViewDelegate { | ||||
|  | ||||
| 	func colorPickerWheelView(didSelectColor color: Color) { | ||||
| 		self.setColor(color) | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										83
									
								
								Tweaks/Alderis/Alderis/ColorPickerNumericSlider.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,83 @@ | ||||
| // | ||||
| //  ColorPickerNumericSlider.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Kabir Oberai on 28/03/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class ColorPickerNumericSlider: ColorPickerComponentSlider { | ||||
|  | ||||
| 	private var textField: UITextField! | ||||
|  | ||||
| 	override init(component: Color.Component, overrideSmartInvert: Bool) { | ||||
| 		super.init(component: component, overrideSmartInvert: overrideSmartInvert) | ||||
|  | ||||
| 		stackView.alignment = .fill | ||||
| 		stackView.spacing = UIFloat(8) | ||||
|  | ||||
| 		let label = UILabel() | ||||
| 		label.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		label.font = UIFont.systemFont(ofSize: UIFloat(16), weight: .medium) | ||||
| 		label.text = component.title | ||||
| 		stackView.insertArrangedSubview(label, at: 0) | ||||
|  | ||||
| 		textField = UITextField() | ||||
| 		textField.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		textField.delegate = self | ||||
| 		textField.returnKeyType = .next | ||||
| 		textField.keyboardType = .numberPad | ||||
| 		textField.autocapitalizationType = .none | ||||
| 		textField.autocorrectionType = .no | ||||
| 		textField.spellCheckingType = .no | ||||
| 		textField.textAlignment = .right | ||||
| 		textField.font = Assets.niceMonospaceDigitFont(ofSize: UIFloat(16)) | ||||
| 		stackView.addArrangedSubview(textField) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			label.widthAnchor.constraint(equalToConstant: UIFloat(50)), | ||||
| 			textField.widthAnchor.constraint(equalToConstant: UIFloat(35)) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	override func setColor(_ color: Color) { | ||||
| 		super.setColor(color) | ||||
| 		textField.text = "\(Int((color[keyPath: component.keyPath] * component.limit).rounded()))" | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| extension ColorPickerNumericSlider: UITextFieldDelegate { | ||||
|  | ||||
| 	func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { | ||||
| 		let newString = textField.text!.replacingCharacters(in: Range(range, in: textField.text!)!, with: string) | ||||
| 		guard !newString.isEmpty else { return true } | ||||
|  | ||||
| 		// Numeric only, 0-limit | ||||
| 		let badCharacterSet = CharacterSet(charactersIn: "0123456789").inverted | ||||
| 		guard newString.rangeOfCharacter(from: badCharacterSet) == nil else { | ||||
| 			beep() | ||||
| 			return false | ||||
| 		} | ||||
| 		let limit = component.limit | ||||
| 		guard let value = Int(newString), 0...limit ~= CGFloat(value) else { | ||||
| 			beep() | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		// Run this after the input is fully processed by enqueuing it onto the run loop | ||||
| 		OperationQueue.main.addOperation { | ||||
| 			self.value = CGFloat(value) / limit | ||||
| 			self.sliderChanged() | ||||
| 		} | ||||
|  | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										206
									
								
								Tweaks/Alderis/Alderis/ColorPickerSlider.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,206 @@ | ||||
| // | ||||
| //  ColorPickerSlider.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Kabir Oberai on 28/03/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class ColorPickerSliderBase: UIControl { | ||||
|  | ||||
| 	var overrideSmartInvert: Bool { | ||||
| 		didSet { | ||||
| 			slider.accessibilityIgnoresInvertColors = overrideSmartInvert | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	let stackView: UIStackView | ||||
| 	let slider: ColorSlider | ||||
|  | ||||
| 	var value: CGFloat { | ||||
| 		get { CGFloat(slider.value) } | ||||
| 		set { slider.value = Float(newValue) } | ||||
| 	} | ||||
|  | ||||
| 	init(overrideSmartInvert: Bool) { | ||||
| 		self.overrideSmartInvert = overrideSmartInvert | ||||
|  | ||||
| 		slider = ColorSlider() | ||||
| 		slider.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		slider.accessibilityIgnoresInvertColors = overrideSmartInvert | ||||
|  | ||||
| 		stackView = UIStackView(arrangedSubviews: [slider]) | ||||
| 		stackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		stackView.axis = .horizontal | ||||
| 		stackView.distribution = .fill | ||||
|  | ||||
| 		super.init(frame: .zero) | ||||
|  | ||||
| 		slider.addTarget(self, action: #selector(sliderChanged), for: .valueChanged) | ||||
| 		addSubview(stackView) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor), | ||||
| 			stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor), | ||||
| 			stackView.topAnchor.constraint(equalTo: self.topAnchor), | ||||
| 			stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	@objc internal func sliderChanged() { | ||||
| 		sendActions(for: .valueChanged) | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| internal protocol ColorPickerSliderProtocol: ColorPickerSliderBase { | ||||
| 	func setColor(_ color: Color) | ||||
| 	func apply(to color: inout Color) | ||||
| } | ||||
|  | ||||
| internal typealias ColorPickerSlider = ColorPickerSliderBase & ColorPickerSliderProtocol | ||||
|  | ||||
| internal class ColorPickerComponentSlider: ColorPickerSlider { | ||||
|  | ||||
| 	let component: Color.Component | ||||
|  | ||||
| 	init(component: Color.Component, overrideSmartInvert: Bool) { | ||||
| 		self.component = component | ||||
| 		super.init(overrideSmartInvert: overrideSmartInvert) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	func setColor(_ color: Color) { | ||||
| 		value = color[keyPath: component.keyPath] | ||||
| 		slider.color = color.uiColor | ||||
| 		slider.gradientColors = component.sliderTintColor(for: color).map(\.uiColor) | ||||
| 	} | ||||
|  | ||||
| 	func apply(to color: inout Color) { | ||||
| 		color[keyPath: component.keyPath] = value | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| internal class ColorSlider: UISlider { | ||||
| 	private let thumbImage = UIGraphicsImageRenderer(size: CGSize(width: 26, height: 26)).image { _ in } | ||||
|  | ||||
| 	var gradientColors = [UIColor]() { | ||||
| 		didSet { gradientView.gradientLayer.colors = gradientColors.map(\.cgColor) } | ||||
| 	} | ||||
|  | ||||
| 	var color: UIColor? { | ||||
| 		get { selectionView?.color } | ||||
| 		set { selectionView?.color = newValue } | ||||
| 	} | ||||
|  | ||||
| 	private var checkerboardView: UIView! | ||||
| 	private var gradientView: GradientView! | ||||
|  | ||||
| 	private var selectionView: ColorWell? | ||||
| 	private var selectionViewXConstraint: NSLayoutConstraint? | ||||
| 	private var valueObserver: NSKeyValueObservation? | ||||
|  | ||||
| 	override init(frame: CGRect) { | ||||
| 		super.init(frame: frame) | ||||
|  | ||||
| 		#if swift(>=5.5) | ||||
| 		var useSliderTrack = !isCatalystMac | ||||
| 		if #available(iOS 15, *) { | ||||
| 			preferredBehavioralStyle = .pad | ||||
| 			useSliderTrack = true | ||||
| 		} | ||||
| 		#else | ||||
| 		let useSliderTrack = true | ||||
| 		#endif | ||||
| 		if useSliderTrack { | ||||
| 			setMinimumTrackImage(UIImage(), for: .normal) | ||||
| 			setMaximumTrackImage(UIImage(), for: .normal) | ||||
| 			setThumbImage(thumbImage, for: .normal) | ||||
| 		} | ||||
|  | ||||
| 		checkerboardView = UIView() | ||||
| 		checkerboardView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		checkerboardView.backgroundColor = Assets.checkerboardPatternColor | ||||
| 		checkerboardView.clipsToBounds = true | ||||
| 		if #available(iOS 13, *) { | ||||
| 			checkerboardView.layer.cornerCurve = .continuous | ||||
| 		} | ||||
| 		insertSubview(checkerboardView, at: 0) | ||||
|  | ||||
| 		gradientView = GradientView() | ||||
| 		gradientView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		gradientView.gradientLayer.startPoint = CGPoint(x: 0, y: 0) | ||||
| 		gradientView.gradientLayer.endPoint = CGPoint(x: 1, y: 0) | ||||
| 		gradientView.gradientLayer.allowsGroupOpacity = false | ||||
| 		checkerboardView.addSubview(gradientView) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			checkerboardView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: UIFloat(-3)), | ||||
| 			checkerboardView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: UIFloat(3)), | ||||
| 			checkerboardView.topAnchor.constraint(equalTo: self.topAnchor, constant: -1), | ||||
| 			checkerboardView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 1), | ||||
|  | ||||
| 			gradientView.leadingAnchor.constraint(equalTo: checkerboardView.leadingAnchor), | ||||
| 			gradientView.trailingAnchor.constraint(equalTo: checkerboardView.trailingAnchor), | ||||
| 			gradientView.topAnchor.constraint(equalTo: checkerboardView.topAnchor), | ||||
| 			gradientView.bottomAnchor.constraint(equalTo: checkerboardView.bottomAnchor), | ||||
| 		]) | ||||
|  | ||||
| 		if useSliderTrack { | ||||
| 			let selectionView = ColorWell() | ||||
| 			selectionView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			selectionView.isDragInteractionEnabled = false | ||||
| 			selectionView.isDropInteractionEnabled = false | ||||
| 			#if swift(>=5.3) | ||||
| 			if #available(iOS 14, *) { | ||||
| 				selectionView.isContextMenuInteractionEnabled = false | ||||
| 			} | ||||
| 			#endif | ||||
| 			insertSubview(selectionView, aboveSubview: checkerboardView) | ||||
| 			self.selectionView = selectionView | ||||
|  | ||||
| 			selectionViewXConstraint = selectionView.leadingAnchor.constraint(equalTo: checkerboardView.leadingAnchor) | ||||
|  | ||||
| 			// Remove minimum width constraint configured by ColorWell internally | ||||
| 			let selectionWidthConstraint = selectionView.constraints.first { $0.firstAnchor == selectionView.widthAnchor } | ||||
| 			selectionWidthConstraint?.isActive = false | ||||
|  | ||||
| 			NSLayoutConstraint.activate([ | ||||
| 				selectionViewXConstraint!, | ||||
| 				selectionView.centerYAnchor.constraint(equalTo: self.centerYAnchor), | ||||
| 				selectionView.widthAnchor.constraint(equalToConstant: UIFloat(24)) | ||||
| 			]) | ||||
|  | ||||
| 			valueObserver = observe(\.value) { _, _ in self.valueChanged() } | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	override func layoutSubviews() { | ||||
| 		super.layoutSubviews() | ||||
| 		checkerboardView.layer.cornerRadius = checkerboardView.frame.size.height / 2 | ||||
| 		valueChanged() | ||||
| 	} | ||||
|  | ||||
| 	private func valueChanged() { | ||||
| 		guard let selectionView = selectionView, | ||||
| 					let selectionViewXConstraint = selectionViewXConstraint else { | ||||
| 			return | ||||
| 		} | ||||
| 		let spacing = frame.size.height - selectionView.frame.size.height | ||||
| 		selectionViewXConstraint.constant = (spacing / 2) + ((frame.size.width - selectionView.frame.size.width - spacing) * CGFloat(value)) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										266
									
								
								Tweaks/Alderis/Alderis/ColorPickerSlidersViewController.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,266 @@ | ||||
| // | ||||
| //  ColorPickerSlidersViewController.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 14/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
| import AudioToolbox | ||||
|  | ||||
| internal class ColorPickerSlidersViewController: ColorPickerTabViewController { | ||||
|  | ||||
| 	static let title = "Sliders" | ||||
| 	static let imageName = "slider.horizontal.3" | ||||
|  | ||||
| 	private enum Mode: CaseIterable { | ||||
| 		case rgb, hsl, hsb, white | ||||
|  | ||||
| 		var title: String { | ||||
| 			switch self { | ||||
| 			case .rgb:   return "RGB" | ||||
| 			case .hsl:   return "HSL" | ||||
| 			case .hsb:   return "HSB" | ||||
| 			case .white: return "White" | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		private var components: [Color.Component] { | ||||
| 			switch self { | ||||
| 			case .rgb:   return [.red, .green, .blue, .alpha] | ||||
| 			case .hsl:   return [.hue, .hslSaturation, .lightness, .alpha] | ||||
| 			case .hsb:   return [.hue, .saturation, .brightness, .alpha] | ||||
| 			case .white: return [.white, .alpha] | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		func makeSliders(overrideSmartInvert: Bool, supportsAlpha: Bool) -> [ColorPickerNumericSlider] { | ||||
| 			components.compactMap { component in | ||||
| 				if component.keyPath == \.alpha && !supportsAlpha { | ||||
| 					return nil | ||||
| 				} | ||||
| 				return ColorPickerNumericSlider(component: component, overrideSmartInvert: overrideSmartInvert) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private var mode: Mode = .rgb { | ||||
| 		didSet { updateMode() } | ||||
| 	} | ||||
|  | ||||
| 	private var segmentedControl: UISegmentedControl! | ||||
|  | ||||
| 	private var allSliders = [Mode: [ColorPickerNumericSlider]]() | ||||
| 	private var sliderStacks = [Mode: UIStackView]() | ||||
|  | ||||
| 	private let colorWell = ColorWell() | ||||
|  | ||||
| 	private var hexTextField: UITextField! | ||||
| 	private var hexOptions = Color.HexOptions() | ||||
|  | ||||
| 	private var eggLabel: UILabel! | ||||
| 	private var eggString = "" | ||||
|  | ||||
| 	override func viewDidLoad() { | ||||
| 		super.viewDidLoad() | ||||
|  | ||||
| 		let segmentedControlContainer = UIView() | ||||
| 		segmentedControlContainer.translatesAutoresizingMaskIntoConstraints = false | ||||
|  | ||||
| 		segmentedControl = UISegmentedControl(items: Mode.allCases.map(\.title)) | ||||
| 		segmentedControl.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		segmentedControl.selectedSegmentIndex = 0 | ||||
| 		segmentedControl.addTarget(self, action: #selector(segmentControlChanged(_:)), for: .valueChanged) | ||||
| 		segmentedControlContainer.addSubview(segmentedControl) | ||||
|  | ||||
| 		let topSpacerView = UIView() | ||||
| 		topSpacerView.translatesAutoresizingMaskIntoConstraints = false | ||||
|  | ||||
| 		let mainStackView = UIStackView(arrangedSubviews: [segmentedControlContainer, topSpacerView]) | ||||
| 		mainStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		mainStackView.axis = .vertical | ||||
| 		mainStackView.alignment = .fill | ||||
| 		mainStackView.distribution = .fill | ||||
| 		mainStackView.spacing = UIFloat(6) | ||||
| 		view.addSubview(mainStackView) | ||||
|  | ||||
| 		for mode in Mode.allCases { | ||||
| 			let modeSliders = mode.makeSliders(overrideSmartInvert: configuration.overrideSmartInvert, | ||||
| 																				 supportsAlpha: configuration.supportsAlpha) | ||||
| 			for slider in modeSliders { | ||||
| 				slider.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged) | ||||
| 			} | ||||
| 			allSliders[mode] = modeSliders | ||||
|  | ||||
| 			let sliderStackView = UIStackView(arrangedSubviews: modeSliders) | ||||
| 			sliderStackView.axis = .vertical | ||||
| 			sliderStackView.alignment = .fill | ||||
| 			sliderStackView.distribution = .fill | ||||
| 			sliderStackView.spacing = UIFloat(10) | ||||
| 			sliderStacks[mode] = sliderStackView | ||||
| 			mainStackView.addArrangedSubview(sliderStackView) | ||||
| 		} | ||||
|  | ||||
| 		colorWell.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 		colorWell.isDragInteractionEnabled = true | ||||
| 		colorWell.isDropInteractionEnabled = false | ||||
|  | ||||
| 		hexTextField = UITextField() | ||||
| 		hexTextField.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		hexTextField.delegate = self | ||||
| 		hexTextField.textAlignment = .right | ||||
| 		hexTextField.returnKeyType = .done | ||||
| 		hexTextField.autocapitalizationType = .none | ||||
| 		hexTextField.autocorrectionType = .no | ||||
| 		hexTextField.spellCheckingType = .no | ||||
| 		hexTextField.font = Assets.niceMonospaceDigitFont(ofSize: UIFloat(16)) | ||||
| 		hexTextField.setContentHuggingPriority(.required, for: .vertical) | ||||
| 		hexTextField.setContentHuggingPriority(.defaultHigh, for: .horizontal) | ||||
|  | ||||
| 		eggLabel = UILabel() | ||||
| 		eggLabel.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		eggLabel.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 		eggLabel.font = UIFont.systemFont(ofSize: UIFloat(24), weight: .heavy) | ||||
| 		eggLabel.isHidden = true | ||||
|  | ||||
| 		let bottomSpacerView = UIView() | ||||
| 		bottomSpacerView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		mainStackView.addArrangedSubview(bottomSpacerView) | ||||
|  | ||||
| 		let hexStackView = UIStackView(arrangedSubviews: [colorWell, eggLabel, hexTextField]) | ||||
| 		hexStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		hexStackView.axis = .horizontal | ||||
| 		hexStackView.alignment = .fill | ||||
| 		hexStackView.distribution = .fill | ||||
| 		hexStackView.spacing = UIFloat(10) | ||||
| 		mainStackView.addArrangedSubview(hexStackView) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			mainStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: UIFloat(15)), | ||||
| 			mainStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: UIFloat(-15)), | ||||
| 			mainStackView.topAnchor.constraint(equalTo: view.topAnchor, constant: UIFloat(15)), | ||||
| 			mainStackView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: UIFloat(-15)), | ||||
|  | ||||
| 			segmentedControl.topAnchor.constraint(equalTo: segmentedControlContainer.topAnchor), | ||||
| 			segmentedControl.bottomAnchor.constraint(equalTo: segmentedControlContainer.bottomAnchor), | ||||
| 			segmentedControl.centerXAnchor.constraint(equalTo: segmentedControlContainer.centerXAnchor), | ||||
|  | ||||
| 			topSpacerView.heightAnchor.constraint(equalToConstant: UIFloat(3)), | ||||
| 			bottomSpacerView.heightAnchor.constraint(equalToConstant: UIFloat(3)), | ||||
|  | ||||
| 			colorWell.widthAnchor.constraint(equalToConstant: UIFloat(32)), | ||||
| 			colorWell.heightAnchor.constraint(equalTo: colorWell.widthAnchor) | ||||
| 		]) | ||||
|  | ||||
| 		updateMode() | ||||
| 	} | ||||
|  | ||||
| 	@objc func segmentControlChanged(_ sender: UISegmentedControl) { | ||||
| 		view.endEditing(true) | ||||
| 		mode = Mode.allCases[sender.selectedSegmentIndex] | ||||
| 	} | ||||
|  | ||||
| 	override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { | ||||
| 		super.touchesBegan(touches, with: event) | ||||
| 		view.endEditing(true) | ||||
| 	} | ||||
|  | ||||
| 	func updateMode() { | ||||
| 		for (stackMode, stack) in sliderStacks { | ||||
| 			stack.isHidden = stackMode != mode | ||||
| 		} | ||||
| 		colorDidChange() | ||||
| 	} | ||||
|  | ||||
| 	func setColor(_ color: Color, hexOptions: Color.HexOptions, shouldBroadcast: Bool = true) { | ||||
| 		self.hexOptions = hexOptions | ||||
| 		super.setColor(color, shouldBroadcast: shouldBroadcast) | ||||
| 	} | ||||
|  | ||||
| 	override func setColor(_ color: Color, shouldBroadcast: Bool = true) { | ||||
| 		self.setColor(color, hexOptions: [], shouldBroadcast: shouldBroadcast) | ||||
| 	} | ||||
|  | ||||
| 	@objc func sliderChanged(_ slider: ColorPickerNumericSlider) { | ||||
| 		var color = self.color | ||||
| 		slider.apply(to: &color) | ||||
| 		setColor(color) | ||||
| 	} | ||||
|  | ||||
| 	override func colorDidChange() { | ||||
| 		allSliders[mode]?.forEach { | ||||
| 			$0.setColor(color) | ||||
| 		} | ||||
|  | ||||
| 		colorWell.color = color.uiColor | ||||
| 		hexTextField.text = color.hexString(with: hexOptions) | ||||
|  | ||||
| 		if #available(iOS 13, *) { | ||||
| 		} else { | ||||
| 			let foregroundColor: UIColor = color.isDark ? .white : .black | ||||
| 			segmentedControl.setTitleTextAttributes([ | ||||
| 				.foregroundColor: foregroundColor | ||||
| 			], for: .selected) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| extension ColorPickerSlidersViewController: UITextFieldDelegate { | ||||
|  | ||||
| 	func textFieldShouldReturn(_ textField: UITextField) -> Bool { | ||||
| 		view.endEditing(true) | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { | ||||
| 		let newString = textField.text!.replacingCharacters(in: Range(range, in: textField.text!)!, with: string) | ||||
| 		guard !newString.isEmpty else { return true } | ||||
|  | ||||
| 		// #AAAAAA | ||||
| 		eggString = "\(eggString.suffix(3))\(string)" | ||||
| 		if eggString.lowercased() == "holo" { | ||||
| 			self.setColor(Color(red: 51 / 255, green: 181 / 255, blue: 229 / 255, alpha: 1)) | ||||
| 			eggLabel.text = "Praise DuARTe" | ||||
| 			eggLabel.textColor = color.uiColor | ||||
| 			eggLabel.isHidden = false | ||||
| 			eggString = "" | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		let canonicalizedString = newString.hasPrefix("#") ? newString.dropFirst() : Substring(newString) | ||||
| 		guard canonicalizedString.count <= 8 else { | ||||
| 			beep() | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		let badCharacterSet = CharacterSet(charactersIn: "0123456789ABCDEFabcdef").inverted | ||||
| 		guard canonicalizedString.rangeOfCharacter(from: badCharacterSet) == nil else { | ||||
| 			beep() | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		if canonicalizedString.count != 6 && canonicalizedString.count != 8 { | ||||
| 			// User is probably still typing it out. Don’t do anything yet. | ||||
| 			return true | ||||
| 		} | ||||
|  | ||||
| 		guard var uiColor = UIColor(propertyListValue: "#\(canonicalizedString)") else { | ||||
| 			return true | ||||
| 		} | ||||
|  | ||||
| 		if !configuration.supportsAlpha { | ||||
| 			// Discard the alpha component. | ||||
| 			uiColor = uiColor.withAlphaComponent(1) | ||||
| 		} | ||||
|  | ||||
| 		let color = Color(uiColor: uiColor) | ||||
| 		OperationQueue.main.addOperation { | ||||
| 			self.setColor(color, hexOptions: canonicalizedString.count == 3 ? .allowShorthand : []) | ||||
| 		} | ||||
|  | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										303
									
								
								Tweaks/Alderis/Alderis/ColorPickerSwatchViewController.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,303 @@ | ||||
| // | ||||
| //  ColorPickerSwatchViewController.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 13/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class ColorPickerSwatchViewController: ColorPickerTabViewController { | ||||
|  | ||||
| 	private class ColorLayer: CALayer { | ||||
| 		let color: Color | ||||
| 		init(color: Color) { | ||||
| 			self.color = color | ||||
| 			super.init() | ||||
| 			backgroundColor = color.uiColor.cgColor | ||||
| 		} | ||||
| 		override init(layer: Any) { | ||||
| 			color = Color(white: 1, alpha: 1) | ||||
| 			super.init(layer: layer) | ||||
| 		} | ||||
| 		required init?(coder: NSCoder) { | ||||
| 			fatalError("init(coder:) has not been implemented") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	static let title = "Swatch" | ||||
| 	static let imageName = "square.grid.4x3.fill" | ||||
|  | ||||
| 	static let colorSwatch = [ | ||||
| 		[ | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.921569, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.839216, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.760784, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.678431, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.600000, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.521569, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.439216, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.360784, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.278431, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.200000, alpha: 1), | ||||
| 			Color(hue: 0.000000, saturation: 0.000000, brightness: 0.000000, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.542793, saturation: 1.000000, brightness: 0.290196, alpha: 1), | ||||
| 			Color(hue: 0.612403, saturation: 0.988506, brightness: 0.341176, alpha: 1), | ||||
| 			Color(hue: 0.703704, saturation: 0.915255, brightness: 0.231373, alpha: 1), | ||||
| 			Color(hue: 0.787878, saturation: 0.901640, brightness: 0.239216, alpha: 1), | ||||
| 			Color(hue: 0.937107, saturation: 0.883333, brightness: 0.235294, alpha: 1), | ||||
| 			Color(hue: 0.010989, saturation: 0.989130, brightness: 0.360784, alpha: 1), | ||||
| 			Color(hue: 0.051852, saturation: 1.000000, brightness: 0.352941, alpha: 1), | ||||
| 			Color(hue: 0.096591, saturation: 1.000000, brightness: 0.345098, alpha: 1), | ||||
| 			Color(hue: 0.118217, saturation: 1.000000, brightness: 0.337255, alpha: 1), | ||||
| 			Color(hue: 0.158497, saturation: 1.000000, brightness: 0.400000, alpha: 1), | ||||
| 			Color(hue: 0.179012, saturation: 0.952941, brightness: 0.333333, alpha: 1), | ||||
| 			Color(hue: 0.251773, saturation: 0.758064, brightness: 0.243137, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.539604, saturation: 1.000000, brightness: 0.396078, alpha: 1), | ||||
| 			Color(hue: 0.603825, saturation: 0.991870, brightness: 0.482353, alpha: 1), | ||||
| 			Color(hue: 0.703704, saturation: 0.878049, brightness: 0.321569, alpha: 1), | ||||
| 			Color(hue: 0.789473, saturation: 0.853933, brightness: 0.349020, alpha: 1), | ||||
| 			Color(hue: 0.939614, saturation: 0.811765, brightness: 0.333333, alpha: 1), | ||||
| 			Color(hue: 0.021629, saturation: 1.000000, brightness: 0.513725, alpha: 1), | ||||
| 			Color(hue: 0.055555, saturation: 1.000000, brightness: 0.482353, alpha: 1), | ||||
| 			Color(hue: 0.101093, saturation: 1.000000, brightness: 0.478431, alpha: 1), | ||||
| 			Color(hue: 0.122222, saturation: 1.000000, brightness: 0.470588, alpha: 1), | ||||
| 			Color(hue: 0.158273, saturation: 0.985816, brightness: 0.552941, alpha: 1), | ||||
| 			Color(hue: 0.177469, saturation: 0.915254, brightness: 0.462745, alpha: 1), | ||||
| 			Color(hue: 0.251366, saturation: 0.701148, brightness: 0.341176, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.538732, saturation: 0.993007, brightness: 0.560784, alpha: 1), | ||||
| 			Color(hue: 0.601578, saturation: 1.000000, brightness: 0.662745, alpha: 1), | ||||
| 			Color(hue: 0.719697, saturation: 0.924370, brightness: 0.466667, alpha: 1), | ||||
| 			Color(hue: 0.788333, saturation: 0.806452, brightness: 0.486275, alpha: 1), | ||||
| 			Color(hue: 0.938596, saturation: 0.785124, brightness: 0.474510, alpha: 1), | ||||
| 			Color(hue: 0.023941, saturation: 1.000000, brightness: 0.709804, alpha: 1), | ||||
| 			Color(hue: 0.059730, saturation: 1.000000, brightness: 0.678431, alpha: 1), | ||||
| 			Color(hue: 0.102564, saturation: 1.000000, brightness: 0.662745, alpha: 1), | ||||
| 			Color(hue: 0.123232, saturation: 0.993976, brightness: 0.650980, alpha: 1), | ||||
| 			Color(hue: 0.159864, saturation: 1.000000, brightness: 0.768627, alpha: 1), | ||||
| 			Color(hue: 0.177704, saturation: 0.915151, brightness: 0.647059, alpha: 1), | ||||
| 			Color(hue: 0.255020, saturation: 0.680328, brightness: 0.478431, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.537037, saturation: 1.000000, brightness: 0.705882, alpha: 1), | ||||
| 			Color(hue: 0.599688, saturation: 1.000000, brightness: 0.839216, alpha: 1), | ||||
| 			Color(hue: 0.706284, saturation: 0.824324, brightness: 0.580392, alpha: 1), | ||||
| 			Color(hue: 0.785333, saturation: 0.791139, brightness: 0.619608, alpha: 1), | ||||
| 			Color(hue: 0.938746, saturation: 0.764707, brightness: 0.600000, alpha: 1), | ||||
| 			Color(hue: 0.026549, saturation: 1.000000, brightness: 0.886275, alpha: 1), | ||||
| 			Color(hue: 0.061927, saturation: 1.000000, brightness: 0.854902, alpha: 1), | ||||
| 			Color(hue: 0.103175, saturation: 0.995261, brightness: 0.827451, alpha: 1), | ||||
| 			Color(hue: 0.125000, saturation: 0.995215, brightness: 0.819608, alpha: 1), | ||||
| 			Color(hue: 0.160544, saturation: 1.000000, brightness: 0.960784, alpha: 1), | ||||
| 			Color(hue: 0.179211, saturation: 0.889952, brightness: 0.819608, alpha: 1), | ||||
| 			Color(hue: 0.253968, saturation: 0.668789, brightness: 0.615686, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.542438, saturation: 1.000000, brightness: 0.847059, alpha: 1), | ||||
| 			Color(hue: 0.603018, saturation: 1.000000, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.716435, saturation: 0.808989, brightness: 0.698039, alpha: 1), | ||||
| 			Color(hue: 0.792237, saturation: 0.776596, brightness: 0.737255, alpha: 1), | ||||
| 			Color(hue: 0.942857, saturation: 0.756756, brightness: 0.725490, alpha: 1), | ||||
| 			Color(hue: 0.030627, saturation: 0.917647, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.069281, saturation: 1.000000, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.111549, saturation: 0.996078, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.131093, saturation: 1.000000, brightness: 0.992157, alpha: 1), | ||||
| 			Color(hue: 0.164021, saturation: 0.744094, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.184162, saturation: 0.766949, brightness: 0.925490, alpha: 1), | ||||
| 			Color(hue: 0.260163, saturation: 0.657754, brightness: 0.733333, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.535193, saturation: 0.996032, brightness: 0.988235, alpha: 1), | ||||
| 			Color(hue: 0.601190, saturation: 0.771653, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.707665, saturation: 0.795745, brightness: 0.921569, alpha: 1), | ||||
| 			Color(hue: 0.786096, saturation: 0.769547, brightness: 0.952941, alpha: 1), | ||||
| 			Color(hue: 0.938597, saturation: 0.743478, brightness: 0.901961, alpha: 1), | ||||
| 			Color(hue: 0.017143, saturation: 0.686275, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.056466, saturation: 0.717647, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.102094, saturation: 0.751968, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.122396, saturation: 0.755906, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.157658, saturation: 0.580392, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.179952, saturation: 0.577406, brightness: 0.937255, alpha: 1), | ||||
| 			Color(hue: 0.254310, saturation: 0.549763, brightness: 0.827451, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.537255, saturation: 0.674603, brightness: 0.988235, alpha: 1), | ||||
| 			Color(hue: 0.605516, saturation: 0.545098, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.719048, saturation: 0.688976, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.790419, saturation: 0.657481, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.940000, saturation: 0.525210, brightness: 0.933333, alpha: 1), | ||||
| 			Color(hue: 0.013333, saturation: 0.490196, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.051282, saturation: 0.509804, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.098039, saturation: 0.533333, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.120098, saturation: 0.533333, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.157321, saturation: 0.419608, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.180135, saturation: 0.409091, brightness: 0.949020, alpha: 1), | ||||
| 			Color(hue: 0.256097, saturation: 0.371041, brightness: 0.866667, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.540881, saturation: 0.418972, brightness: 0.992157, alpha: 1), | ||||
| 			Color(hue: 0.607954, saturation: 0.345098, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.720760, saturation: 0.448818, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.790124, saturation: 0.425197, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.941667, saturation: 0.327869, brightness: 0.956863, alpha: 1), | ||||
| 			Color(hue: 0.012500, saturation: 0.313725, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.051587, saturation: 0.329412, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.093869, saturation: 0.341176, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.116279, saturation: 0.338582, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.157143, saturation: 0.274510, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.179687, saturation: 0.259109, brightness: 0.968627, alpha: 1), | ||||
| 			Color(hue: 0.254902, saturation: 0.219828, brightness: 0.909804, alpha: 1), | ||||
| 		], | ||||
| 		[ | ||||
| 			Color(hue: 0.548077, saturation: 0.203922, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.609848, saturation: 0.172549, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.716981, saturation: 0.208661, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.783019, saturation: 0.207843, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.942983, saturation: 0.152611, brightness: 0.976471, alpha: 1), | ||||
| 			Color(hue: 0.012821, saturation: 0.152941, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.048781, saturation: 0.160784, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.093023, saturation: 0.168627, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.115080, saturation: 0.164706, brightness: 1.000000, alpha: 1), | ||||
| 			Color(hue: 0.156566, saturation: 0.129921, brightness: 0.996078, alpha: 1), | ||||
| 			Color(hue: 0.182796, saturation: 0.123999, brightness: 0.980392, alpha: 1), | ||||
| 			Color(hue: 0.262820, saturation: 0.109243, brightness: 0.933333, alpha: 1), | ||||
| 		] | ||||
| 	] | ||||
|  | ||||
| 	let colors = ColorPickerSwatchViewController.colorSwatch | ||||
|  | ||||
| 	private var colorRows = [[ColorLayer]]() | ||||
| 	private var colorDict = [String: ColorLayer]() | ||||
|  | ||||
| 	var containerView: UIView! | ||||
| 	var selectionView: UIView! | ||||
| 	var containerViewHeightConstraint: NSLayoutConstraint! | ||||
| 	var selectionViewWidthConstraint: NSLayoutConstraint! | ||||
| 	var selectionViewXConstraint: NSLayoutConstraint! | ||||
| 	var selectionViewYConstraint: NSLayoutConstraint! | ||||
|  | ||||
| 	override func viewDidLoad() { | ||||
| 		super.viewDidLoad() | ||||
|  | ||||
| 		containerView = UIView() | ||||
| 		containerView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		containerView.accessibilityIgnoresInvertColors = configuration.overrideSmartInvert | ||||
| 		view.addSubview(containerView) | ||||
|  | ||||
| 		for row in colors { | ||||
| 			var colorRow = [ColorLayer]() | ||||
| 			for color in row { | ||||
| 				let colorLayer = ColorLayer(color: color) | ||||
| 				containerView.layer.addSublayer(colorLayer) | ||||
| 				colorDict[color.hexString()] = colorLayer | ||||
| 				colorRow.append(colorLayer) | ||||
| 			} | ||||
| 			colorRows.append(colorRow) | ||||
| 		} | ||||
|  | ||||
| 		view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(gestureRecognizerFired(_:)))) | ||||
| 		let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(gestureRecognizerFired(_:))) | ||||
| 		panGestureRecognizer.maximumNumberOfTouches = 1 | ||||
| 		view.addGestureRecognizer(panGestureRecognizer) | ||||
|  | ||||
| 		selectionView = UIView() | ||||
| 		selectionView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		selectionView.isUserInteractionEnabled = false | ||||
| 		selectionView.layer.borderColor = UIColor.white.cgColor | ||||
| 		selectionView.layer.borderWidth = 2 | ||||
| 		selectionView.layer.shadowOffset = CGSize(width: 0, height: 0) | ||||
| 		selectionView.layer.shadowOpacity = 1 | ||||
| 		selectionView.layer.shadowColor = UIColor(white: 0, alpha: 0.1).cgColor | ||||
| 		view.addSubview(selectionView) | ||||
|  | ||||
| 		containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: 0) | ||||
| 		selectionViewWidthConstraint = selectionView.widthAnchor.constraint(equalToConstant: UIFloat(20)) | ||||
| 		let selectionViewBaseXConstraint = selectionView.leftAnchor.constraint(equalTo: view.leftAnchor) | ||||
| 		selectionViewBaseXConstraint.priority = .defaultLow | ||||
| 		let selectionViewBaseYConstraint = selectionView.topAnchor.constraint(equalTo: view.topAnchor) | ||||
| 		selectionViewBaseYConstraint.priority = .defaultLow | ||||
| 		selectionViewXConstraint = selectionView.leftAnchor.constraint(equalTo: view.leftAnchor) | ||||
| 		selectionViewYConstraint = selectionView.topAnchor.constraint(equalTo: view.topAnchor) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), | ||||
| 			containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), | ||||
| 			containerView.topAnchor.constraint(equalTo: view.topAnchor), | ||||
| 			containerView.bottomAnchor.constraint(greaterThanOrEqualTo: view.bottomAnchor), | ||||
| 			containerViewHeightConstraint, | ||||
|  | ||||
| 			selectionViewWidthConstraint, | ||||
| 			selectionView.heightAnchor.constraint(equalTo: selectionView.widthAnchor), | ||||
| 			selectionViewBaseXConstraint, | ||||
| 			selectionViewBaseYConstraint, | ||||
| 			selectionViewXConstraint, | ||||
| 			selectionViewYConstraint | ||||
| 		]) | ||||
|  | ||||
| 		colorDidChange() | ||||
| 	} | ||||
|  | ||||
| 	override func viewDidLayoutSubviews() { | ||||
| 		super.viewDidLayoutSubviews() | ||||
|  | ||||
| 		var x: CGFloat = 0, y: CGFloat = 0 | ||||
| 		let size = view.frame.size.width / CGFloat(colors[0].count) | ||||
| 		for row in colorRows { | ||||
| 			x = 0 | ||||
| 			for item in row { | ||||
| 				item.frame = CGRect(x: x * size, y: y * size, width: size, height: size) | ||||
| 				x += 1 | ||||
| 			} | ||||
| 			y += 1 | ||||
| 		} | ||||
|  | ||||
| 		containerViewHeightConstraint.constant = y * size | ||||
| 		selectionViewWidthConstraint.constant = size | ||||
| 		UIView.performWithoutAnimation { | ||||
| 			colorDidChange() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@objc private func gestureRecognizerFired(_ sender: UIGestureRecognizer) { | ||||
| 		switch sender.state { | ||||
| 		case .began, .changed, .ended: | ||||
| 			let location = sender.location(in: containerView) | ||||
| 			guard let colorView = containerView.layer.hitTest(location) as? ColorLayer else { | ||||
| 				return | ||||
| 			} | ||||
| 			self.setColor(colorView.color) | ||||
| 		case .possible, .cancelled, .failed: | ||||
| 			break | ||||
| 		@unknown default: | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	func setSelection(to colorLayer: CALayer?) { | ||||
| 		let wasHidden = selectionView.isHidden | ||||
| 		selectionView.isHidden = colorLayer == nil | ||||
| 		selectionViewXConstraint.constant = colorLayer?.frame.origin.x ?? 0 | ||||
| 		selectionViewYConstraint.constant = colorLayer?.frame.origin.y ?? 0 | ||||
| 		if wasHidden { | ||||
| 			view.layoutIfNeeded() | ||||
| 		} else { | ||||
| 			UIView.animate(withDuration: 0.2) { | ||||
| 				self.view.layoutIfNeeded() | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	override func colorDidChange() { | ||||
| 		guard selectionView != nil else { return } | ||||
| 		setSelection(to: colorDict[color.hexString()]) | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										60
									
								
								Tweaks/Alderis/Alderis/ColorPickerTabViewController.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,60 @@ | ||||
| // | ||||
| //  ColorPickerTabViewController.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Kabir Oberai on 23/03/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal protocol ColorPickerTabDelegate: AnyObject { | ||||
| 	func colorPickerTab(_ tab: ColorPickerTabViewControllerBase, didSelect color: Color) | ||||
| } | ||||
|  | ||||
| internal class ColorPickerTabViewControllerBase: UIViewController { | ||||
|  | ||||
| 	unowned var tabDelegate: ColorPickerTabDelegate | ||||
|  | ||||
| 	private(set) var configuration: ColorPickerConfiguration | ||||
|  | ||||
| 	private(set) var color: Color { | ||||
| 		didSet { colorDidChange() } | ||||
| 	} | ||||
|  | ||||
| 	func colorDidChange() {} | ||||
|  | ||||
| 	func setColor(_ color: Color, shouldBroadcast: Bool = true) { | ||||
| 		if self.color == color { | ||||
| 			return | ||||
| 		} | ||||
| 		self.color = color | ||||
| 		if shouldBroadcast { | ||||
| 			tabDelegate.colorPickerTab(self, didSelect: color) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	required init(tabDelegate: ColorPickerTabDelegate, configuration: ColorPickerConfiguration, color: Color) { | ||||
| 		self.tabDelegate = tabDelegate | ||||
| 		self.configuration = configuration | ||||
| 		self.color = color | ||||
| 		super.init(nibName: nil, bundle: nil) | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| internal protocol ColorPickerTabViewControllerProtocol: ColorPickerTabViewControllerBase { | ||||
| 	static var title: String { get } | ||||
| 	static var imageName: String { get } | ||||
| 	static var image: UIImage { get } | ||||
| } | ||||
|  | ||||
| extension ColorPickerTabViewControllerProtocol { | ||||
| 	static var image: UIImage { Assets.systemImage(named: imageName, font: .systemFont(ofSize: UIFloat(20), weight: .medium)) ?? UIImage() } | ||||
| } | ||||
|  | ||||
| internal typealias ColorPickerTabViewController = ColorPickerTabViewControllerBase & ColorPickerTabViewControllerProtocol | ||||
							
								
								
									
										385
									
								
								Tweaks/Alderis/Alderis/ColorPickerViewController.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,385 @@ | ||||
| // | ||||
| //  ColorPickerViewController.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 12/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| /// Provides the Color Picker user interface. | ||||
| /// | ||||
| /// Present this view controller to display the color picker. Do not push it onto a navigation | ||||
| /// controller stack. In horizontally and vertically regular size class environments, for instance | ||||
| /// on iPad and Mac, the picker will be presented as a popover. This means that you must set | ||||
| /// `sourceView` or other similar properties on the view controller’s `popoverPresentationController` | ||||
| /// before presentation. | ||||
| /// | ||||
| /// To review examples of `ColorPickerViewController` in use, run `pod try Alderis`. | ||||
| @objc(HBColorPickerViewController) | ||||
| open class ColorPickerViewController: UIViewController { | ||||
|  | ||||
| 	/// Do not rely on this fallback value - always specify a color! | ||||
| 	private static let defaultColor = UIColor(white: 0.6, alpha: 1) | ||||
|  | ||||
| 	/// Initialise an instance of `ColorPickerViewController` with a configuration object. | ||||
| 	/// | ||||
| 	/// Remember to set the `delegate` before presenting the view controller. | ||||
| 	@objc public init(configuration: ColorPickerConfiguration) { | ||||
| 		self.configuration = configuration | ||||
| 		super.init(nibName: nil, bundle: nil) | ||||
| 		setUp() | ||||
| 	} | ||||
|  | ||||
| 	/// The delegate that will receive the user’s selection upon tapping the Done button, or a | ||||
| 	/// cancellation upon tapping the Cancel button. | ||||
| 	@objc open weak var delegate: ColorPickerDelegate? { | ||||
| 		didSet { innerViewController?.delegate = delegate } | ||||
| 	} | ||||
|  | ||||
| 	/// The configuration of the color picker. Use this to set the initially selected color, as well | ||||
| 	/// as other behavioral options. | ||||
| 	/// | ||||
| 	/// Making changes to this value or its properties after the color picker interface has been | ||||
| 	/// presented may result in undefined behavior. | ||||
| 	/// | ||||
| 	/// - see: `ColorPickerConfiguration` | ||||
| 	@objc open var configuration: ColorPickerConfiguration! | ||||
|  | ||||
| 	/// Deprecated. Set `ColorPickerConfiguration.overrideSmartInvert` instead. | ||||
| 	/// | ||||
| 	/// - see: `ColorPickerConfiguration.overrideSmartInvert` | ||||
| 	@available(*, deprecated, message: "Use ColorPickerConfiguration instead") | ||||
| 	@objc open var overrideSmartInvert = true | ||||
|  | ||||
| 	/// Deprecated. Set `ColorPickerConfiguration.color` instead. | ||||
| 	/// | ||||
| 	/// - see: `ColorPickerConfiguration.color` | ||||
| 	@available(*, deprecated, message: "Use ColorPickerConfiguration instead") | ||||
| 	@objc open var color = ColorPickerViewController.defaultColor | ||||
|  | ||||
| 	// A width divisible by 12 (the number of items wide in the swatch). | ||||
| 	private var finalWidth: CGFloat { | ||||
| 		if modalPresentationStyle == .popover { | ||||
| 			return UIFloat(336) | ||||
| 		} else { | ||||
| 			return floor(min(UIFloat(384), view.frame.size.width - 30) / 12) * 12 | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private var isFullScreen: Bool { modalPresentationStyle != .popover } | ||||
|  | ||||
| 	private var innerViewController: ColorPickerInnerViewController! | ||||
|  | ||||
| 	private var backdropView: UIView! | ||||
| 	private var backgroundView: UIVisualEffectView! | ||||
|  | ||||
| 	private var widthLayoutConstraint: NSLayoutConstraint! | ||||
| 	private var bottomLayoutConstraint: NSLayoutConstraint! | ||||
| 	private var bottomAnimatingLayoutConstraint: NSLayoutConstraint! | ||||
|  | ||||
| 	// swiftlint:disable:next weak_delegate | ||||
| 	private lazy var _transitioningDelegate = BottomSheetTransitioningDelegate() | ||||
|  | ||||
| 	private var initialBottomSafeAreaInset: CGFloat? | ||||
| 	private var isKeyboardVisible = false | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { | ||||
| 		self.configuration = nil | ||||
| 		super.init(nibName: nil, bundle: nil) | ||||
| 		setUp() | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	required public init?(coder: NSCoder) { | ||||
| 		self.configuration = nil | ||||
| 		super.init(coder: coder) | ||||
| 		setUp() | ||||
| 	} | ||||
|  | ||||
| 	private func setUp() { | ||||
| 		if traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass == .regular { | ||||
| 			modalPresentationStyle = .popover | ||||
| 		} else { | ||||
| 			modalPresentationStyle = .overCurrentContext | ||||
| 			transitioningDelegate = _transitioningDelegate | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func viewDidLoad() { | ||||
| 		super.viewDidLoad() | ||||
|  | ||||
| 		var compatibilityMode = false | ||||
| 		if configuration == nil { | ||||
| 			let deprecatedAPI: ColorPickerViewControllerDeprecatedMethods = self | ||||
| 			// Yes, Swift, I know my code for handling deprecated API usage uses deprecated API 🙄 | ||||
| 			if deprecatedAPI.color == ColorPickerViewController.defaultColor { | ||||
| 				fatalError("Alderis: You need to set a configuration. https://hbang.github.io/Alderis/") | ||||
| 			} | ||||
| 			NSLog("Alderis: Deprecated configuration API in use. This will be removed in a future release. Migrate to using ColorPickerConfiguration. https://hbang.github.io/Alderis/") | ||||
| 			compatibilityMode = true | ||||
| 			configuration = ColorPickerConfiguration(color: deprecatedAPI.color) | ||||
| 			configuration.overrideSmartInvert = deprecatedAPI.overrideSmartInvert | ||||
| 		} | ||||
|  | ||||
| 		if !configuration.supportsAlpha { | ||||
| 			// Force the color to be fully opaque. | ||||
| 			configuration.color = configuration.color.withAlphaComponent(1) | ||||
| 		} | ||||
|  | ||||
| 		navigationController?.isNavigationBarHidden = true | ||||
| 		view.backgroundColor = .clear | ||||
| 		preferredContentSize = .zero | ||||
|  | ||||
| 		if isFullScreen { | ||||
| 			backdropView = UIView() | ||||
| 			backdropView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			backdropView.backgroundColor = Assets.backdropColor | ||||
| 			backdropView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dismissGestureFired(_:)))) | ||||
| 			view.addSubview(backdropView) | ||||
| 		} | ||||
|  | ||||
| 		let containerView = UIView() | ||||
| 		containerView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		view.addSubview(containerView) | ||||
|  | ||||
| 		if isFullScreen { | ||||
| 			let style: UIBlurEffect.Style | ||||
| 			if #available(iOS 13, *) { | ||||
| 				style = .systemThinMaterial | ||||
| 			} else { | ||||
| 				style = .light | ||||
| 			} | ||||
| 			backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: style)) | ||||
| 			backgroundView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 			backgroundView.clipsToBounds = true | ||||
| 			backgroundView.layer.cornerRadius = 13 | ||||
| 			if #available(iOS 13, *) { | ||||
| 				backgroundView.layer.cornerCurve = .continuous | ||||
| 			} | ||||
| 			containerView.addSubview(backgroundView) | ||||
| 		} | ||||
|  | ||||
| 		innerViewController = ColorPickerInnerViewController(delegate: delegate, configuration: configuration) | ||||
| 		innerViewController.compatibilityMode = compatibilityMode | ||||
| 		innerViewController.willMove(toParent: self) | ||||
| 		addChild(innerViewController) | ||||
| 		innerViewController.view.translatesAutoresizingMaskIntoConstraints = false | ||||
|  | ||||
| 		if isFullScreen { | ||||
| 			innerViewController.view.clipsToBounds = true | ||||
| 			innerViewController.view.layer.cornerRadius = 13 | ||||
| 			innerViewController.view.layer.borderWidth = 1 | ||||
| 			innerViewController.view.layer.borderColor = Assets.borderColor.cgColor | ||||
| 			if #available(iOS 13, *) { | ||||
| 				innerViewController.view.layer.cornerCurve = .continuous | ||||
| 			} | ||||
| 		} else { | ||||
| 			popoverPresentationController?.delegate = innerViewController | ||||
| 		} | ||||
|  | ||||
| 		containerView.addSubview(innerViewController.view) | ||||
|  | ||||
| 		widthLayoutConstraint = containerView.widthAnchor.constraint(equalToConstant: finalWidth) | ||||
| 		bottomLayoutConstraint = view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0) | ||||
| 		bottomAnimatingLayoutConstraint = view.bottomAnchor.constraint(equalTo: containerView.topAnchor) | ||||
|  | ||||
| 		NSLayoutConstraint.activate( | ||||
| 			[ | ||||
| 				widthLayoutConstraint, | ||||
| 				innerViewController.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), | ||||
| 				innerViewController.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), | ||||
| 				innerViewController.view.topAnchor.constraint(equalTo: containerView.topAnchor), | ||||
| 				innerViewController.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) | ||||
| 			] + | ||||
| 			(isFullScreen ? [ | ||||
| 				backdropView.leadingAnchor.constraint(equalTo: view.leadingAnchor), | ||||
| 				backdropView.trailingAnchor.constraint(equalTo: view.trailingAnchor), | ||||
| 				backdropView.topAnchor.constraint(equalTo: view.topAnchor), | ||||
| 				backdropView.bottomAnchor.constraint(equalTo: view.bottomAnchor), | ||||
|  | ||||
| 				backgroundView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), | ||||
| 				backgroundView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), | ||||
| 				backgroundView.topAnchor.constraint(equalTo: containerView.topAnchor), | ||||
| 				backgroundView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), | ||||
|  | ||||
| 				containerView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor), | ||||
| 				bottomLayoutConstraint | ||||
| 			] : [ | ||||
| 				 containerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), | ||||
| 				 containerView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), | ||||
| 				 containerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), | ||||
| 				 containerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) | ||||
| 			]) | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func viewWillLayoutSubviews() { | ||||
| 		super.viewWillLayoutSubviews() | ||||
|  | ||||
| 		widthLayoutConstraint.constant = finalWidth | ||||
|  | ||||
| 		if isFullScreen { | ||||
| 			innerViewController.view.layer.borderWidth = 1 / (view.window?.screen.scale ?? 1) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func viewSafeAreaInsetsDidChange() { | ||||
| 		super.viewSafeAreaInsetsDidChange() | ||||
|  | ||||
| 		if !isKeyboardVisible { | ||||
| 			initialBottomSafeAreaInset = view.safeAreaInsets.bottom | ||||
| 			bottomLayoutConstraint.constant = initialBottomSafeAreaInset == 0 ? 15 : 0 | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	open override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) { | ||||
| 		super.preferredContentSizeDidChange(forChildContentContainer: container) | ||||
|  | ||||
| 		if !isFullScreen { | ||||
| 			preferredContentSize = CGSize(width: finalWidth, | ||||
| 																		height: innerViewController.preferredContentSize.height) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func viewWillAppear(_ animated: Bool) { | ||||
| 		super.viewWillAppear(animated) | ||||
|  | ||||
| 		if navigationController != nil && navigationController!.viewControllers.count > 1 { | ||||
| 			assertionFailure("Do not push \(String(describing: type(of: self))) onto a navigation controller stack. It must be presented using UIViewController.present(_:animated:completion:).") | ||||
| 		} | ||||
|  | ||||
| 		if animated && isFullScreen { | ||||
| 			backdropView.alpha = 0 | ||||
| 			bottomLayoutConstraint.isActive = false | ||||
| 			bottomAnimatingLayoutConstraint.isActive = true | ||||
| 			view.layoutIfNeeded() | ||||
|  | ||||
| 			UIView.animate(withDuration: 0.4, | ||||
| 										 delay: 0, | ||||
| 										 usingSpringWithDamping: 2, | ||||
| 										 initialSpringVelocity: 0.5, | ||||
| 										 options: [], | ||||
| 										 animations: { | ||||
| 											self.backdropView.alpha = 1 | ||||
| 											self.bottomLayoutConstraint.isActive = true | ||||
| 											self.bottomAnimatingLayoutConstraint.isActive = false | ||||
| 											self.view.layoutIfNeeded() | ||||
| 										 }, | ||||
| 										 completion: nil) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private let keyboardNotificationNames = [ | ||||
| 		UIResponder.keyboardWillShowNotification, | ||||
| 		UIResponder.keyboardWillHideNotification, | ||||
| 		UIResponder.keyboardWillChangeFrameNotification | ||||
| 	] | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func viewDidAppear(_ animated: Bool) { | ||||
| 		super.viewDidAppear(animated) | ||||
|  | ||||
| 		for name in keyboardNotificationNames { | ||||
| 			NotificationCenter.default.addObserver(self, selector: #selector(keyboardFrameWillChange(_:)), name: name, object: nil) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func viewWillDisappear(_ animated: Bool) { | ||||
| 		super.viewWillDisappear(animated) | ||||
|  | ||||
| 		for name in keyboardNotificationNames { | ||||
| 			NotificationCenter.default.removeObserver(self, name: name, object: nil) | ||||
| 		} | ||||
|  | ||||
| 		if animated && isFullScreen { | ||||
| 			backdropView.alpha = 1 | ||||
| 			bottomLayoutConstraint.isActive = true | ||||
| 			bottomAnimatingLayoutConstraint.isActive = false | ||||
| 			view.layoutIfNeeded() | ||||
|  | ||||
| 			UIView.animate(withDuration: 0.4, | ||||
| 										 delay: 0, | ||||
| 										 usingSpringWithDamping: 0.8, | ||||
| 										 initialSpringVelocity: 0.5, | ||||
| 										 options: [], | ||||
| 										 animations: { | ||||
| 											self.backdropView.alpha = 0 | ||||
| 											self.bottomLayoutConstraint.isActive = false | ||||
| 											self.bottomAnimatingLayoutConstraint.isActive = true | ||||
| 											self.view.layoutIfNeeded() | ||||
| 										 }, | ||||
| 										 completion: nil) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { | ||||
| 		super.traitCollectionDidChange(previousTraitCollection) | ||||
|  | ||||
| 		if isFullScreen { | ||||
| 			// CGColor doesn’t support dynamic colors, so we need to set this again. | ||||
| 			innerViewController.view.layer.borderColor = Assets.borderColor.cgColor | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@objc private func keyboardFrameWillChange(_ notification: Notification) { | ||||
| 		if !isFullScreen { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		guard let userInfo = notification.userInfo, | ||||
| 			let keyboardEndFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect, | ||||
| 			let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval, | ||||
| 			let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt | ||||
| 			else { | ||||
| 				return | ||||
| 		} | ||||
|  | ||||
| 		isKeyboardVisible = notification.name != UIResponder.keyboardWillHideNotification | ||||
|  | ||||
| 		var options: UIView.AnimationOptions = .beginFromCurrentState | ||||
| 		options.insert(.init(rawValue: curve << 16)) | ||||
|  | ||||
| 		UIView.animate(withDuration: duration, | ||||
| 									 delay: 0, | ||||
| 									 options: options, | ||||
| 									 animations: { | ||||
| 										let keyboardHeight: CGFloat = (self.isKeyboardVisible ? keyboardEndFrame.size.height : 0) | ||||
| 										let keyboardExtraMargin: CGFloat = (self.isKeyboardVisible && self.initialBottomSafeAreaInset != 0 ? 15 : 0) | ||||
| 										let bottom = max(keyboardHeight - (self.initialBottomSafeAreaInset ?? 0), 0) + keyboardExtraMargin | ||||
| 										self.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: bottom, right: 0) | ||||
| 										self.view.layoutIfNeeded() | ||||
| 									 }, | ||||
| 									 completion: nil) | ||||
| 	} | ||||
|  | ||||
| 	@objc private func dismissGestureFired(_ gestureRecognizer: UITapGestureRecognizer) { | ||||
| 		if gestureRecognizer.state == .ended { | ||||
| 			if isKeyboardVisible { | ||||
| 				view.endEditing(true) | ||||
| 			} else { | ||||
| 				innerViewController.saveTapped() | ||||
| 				dismiss(animated: true, completion: nil) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| /// :nodoc: | ||||
| private protocol ColorPickerViewControllerDeprecatedMethods { | ||||
| 	var color: UIColor { get } | ||||
| 	var overrideSmartInvert: Bool { get } | ||||
| } | ||||
|  | ||||
| /// :nodoc: | ||||
| extension ColorPickerViewController: ColorPickerViewControllerDeprecatedMethods {} | ||||
							
								
								
									
										242
									
								
								Tweaks/Alderis/Alderis/ColorPickerWheelView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,242 @@ | ||||
| // | ||||
| //  ColorPickerMapView.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 14/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal protocol ColorPickerWheelViewDelegate: AnyObject { | ||||
| 	func colorPickerWheelView(didSelectColor color: Color) | ||||
| } | ||||
|  | ||||
| internal class ColorPickerWheelView: UIView { | ||||
|  | ||||
| 	weak var delegate: ColorPickerWheelViewDelegate? | ||||
|  | ||||
| 	var color: Color { | ||||
| 		didSet { updateColor() } | ||||
| 	} | ||||
|  | ||||
| 	private var containerView: UIView! | ||||
| 	private var wheelView: ColorPickerWheelInnerView! | ||||
| 	private var selectionView: ColorWell! | ||||
| 	private var selectionViewXConstraint: NSLayoutConstraint! | ||||
| 	private var selectionViewYConstraint: NSLayoutConstraint! | ||||
| 	private var selectionViewFingerDownConstraint: NSLayoutConstraint! | ||||
|  | ||||
| 	private var isFingerDown = false | ||||
| 	private let touchDownFeedbackGenerator = UIImpactFeedbackGenerator(style: .medium) | ||||
| 	private let touchUpFeedbackGenerator = UIImpactFeedbackGenerator(style: .light) | ||||
|  | ||||
| 	init(color: Color) { | ||||
| 		self.color = color | ||||
| 		super.init(frame: .zero) | ||||
|  | ||||
| 		containerView = UIView() | ||||
| 		containerView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		containerView.clipsToBounds = true | ||||
| 		addSubview(containerView) | ||||
|  | ||||
| 		containerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(gestureRecognizerFired(_:)))) | ||||
| 		containerView.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(gestureRecognizerFired(_:)))) | ||||
| 		let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(gestureRecognizerFired(_:))) | ||||
| 		panGestureRecognizer.maximumNumberOfTouches = 1 | ||||
| 		containerView.addGestureRecognizer(panGestureRecognizer) | ||||
|  | ||||
| 		wheelView = ColorPickerWheelInnerView() | ||||
| 		wheelView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		wheelView.handleLayout = { [weak self] in self?.setNeedsLayout() } | ||||
| 		containerView.addSubview(wheelView) | ||||
|  | ||||
| 		selectionView = ColorWell() | ||||
| 		selectionView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		selectionView.isDragInteractionEnabled = false | ||||
| 		selectionView.isDropInteractionEnabled = false | ||||
| 		#if swift(>=5.3) | ||||
| 		if #available(iOS 14, *) { | ||||
| 			selectionView.isContextMenuInteractionEnabled = false | ||||
| 		} | ||||
| 		#endif | ||||
| 		containerView.addSubview(selectionView) | ||||
|  | ||||
| 		selectionViewXConstraint = selectionView.leftAnchor.constraint(equalTo: containerView.leftAnchor) | ||||
| 		selectionViewYConstraint = selectionView.topAnchor.constraint(equalTo: containerView.topAnchor) | ||||
| 		// https://www.youtube.com/watch?v=Qs8kDiOwPBA | ||||
| 		selectionViewFingerDownConstraint = selectionView.widthAnchor.constraint(equalToConstant: 56) | ||||
| 		let selectionViewNormalConstraint = selectionView.widthAnchor.constraint(equalToConstant: UIFloat(24)) | ||||
| 		selectionViewNormalConstraint.priority = .defaultHigh | ||||
|  | ||||
| 		// Remove minimum width constraint configured by ColorWell internally | ||||
| 		let selectionWidthConstraint = selectionView.constraints.first { $0.firstAnchor == selectionView.widthAnchor } | ||||
| 		selectionWidthConstraint?.isActive = false | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			containerView.centerXAnchor.constraint(equalTo: self.centerXAnchor), | ||||
| 			containerView.topAnchor.constraint(equalTo: self.topAnchor), | ||||
| 			containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor), | ||||
| 			containerView.widthAnchor.constraint(equalTo: containerView.heightAnchor, constant: UIFloat(30)), | ||||
|  | ||||
| 			wheelView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: UIFloat(30)), | ||||
| 			wheelView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: UIFloat(-30)), | ||||
| 			wheelView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: UIFloat(15)), | ||||
| 			wheelView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: UIFloat(-15)), | ||||
|  | ||||
| 			selectionViewXConstraint, | ||||
| 			selectionViewYConstraint, | ||||
| 			selectionViewNormalConstraint, | ||||
| 			selectionView.heightAnchor.constraint(equalTo: selectionView.widthAnchor) | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	override func layoutSubviews() { | ||||
| 		super.layoutSubviews() | ||||
| 		updateSelectionPoint() | ||||
| 	} | ||||
|  | ||||
| 	private func updateColor() { | ||||
| 		wheelView.brightness = color.brightness | ||||
| 		selectionView.backgroundColor = color.uiColor | ||||
| 		updateSelectionPoint() | ||||
| 	} | ||||
|  | ||||
| 	private func updateSelectionPoint() { | ||||
| 		let colorPoint = pointForColor(color, in: wheelView.frame.size) | ||||
| 		let point = CGPoint(x: wheelView.frame.origin.x + colorPoint.x - (selectionView.frame.size.width / 2), | ||||
| 												y: min( | ||||
| 													frame.size.height - selectionView.frame.size.height - 1, | ||||
| 													max(1, wheelView.frame.origin.y + colorPoint.y - (selectionView.frame.size.height / 2)) | ||||
| 												)) | ||||
| 		selectionViewXConstraint.constant = point.x | ||||
| 		selectionViewYConstraint.constant = point.y | ||||
| 	} | ||||
|  | ||||
| 	private func colorAt(position: CGPoint, in size: CGSize) -> Color { | ||||
| 		let point = CGPoint(x: (size.width / 2) - position.x, | ||||
| 												y: (size.height / 2) - position.y) | ||||
| 		let h = 180 + round(atan2(point.y, point.x) * (180 / .pi)) | ||||
| 		let handleRange = size.width / 2 | ||||
| 		let handleDistance = min(sqrt(point.x * point.x + point.y * point.y), handleRange) | ||||
| 		let s = round(100 / handleRange * handleDistance) | ||||
| 		return Color(hue: h / 360, saturation: s / 100, brightness: color.brightness, alpha: 1) | ||||
| 	} | ||||
|  | ||||
| 	private func pointForColor(_ color: Color, in size: CGSize) -> CGPoint { | ||||
| 		let handleRange = size.width / 2 | ||||
| 		let handleAngle = (color.hue * 360) * (.pi / 180) | ||||
| 		let handleDistance = color.saturation * handleRange | ||||
| 		return CGPoint(x: (size.width / 2) + handleDistance * cos(handleAngle), | ||||
| 									 y: (size.height / 2) + handleDistance * sin(handleAngle)) | ||||
| 	} | ||||
|  | ||||
| 	@objc private func gestureRecognizerFired(_ sender: UIGestureRecognizer) { | ||||
| 		switch sender.state { | ||||
| 		case .began, .changed, .ended: | ||||
| 			var location = sender.location(in: containerView) | ||||
| 			location.x -= wheelView.frame.origin.x | ||||
| 			location.y -= wheelView.frame.origin.y | ||||
| 			color = colorAt(position: location, in: wheelView.frame.size) | ||||
| 			delegate?.colorPickerWheelView(didSelectColor: color) | ||||
| 		case .possible, .cancelled, .failed: | ||||
| 			break | ||||
| 		@unknown default: | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		if sender is UITapGestureRecognizer { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		switch sender.state { | ||||
| 		case .began, .ended, .cancelled: | ||||
| 			isFingerDown = sender.state == .began | ||||
| 			selectionViewFingerDownConstraint.isActive = isFingerDown && !isCatalyst | ||||
| 			updateSelectionPoint() | ||||
| 			UIView.animate(withDuration: 0.2, animations: { | ||||
| 				self.containerView.layoutIfNeeded() | ||||
| 				self.updateSelectionPoint() | ||||
| 			}, completion: { _ in | ||||
| 				self.updateSelectionPoint() | ||||
| 			}) | ||||
| 			if sender.state == .began { | ||||
| 				touchDownFeedbackGenerator.impactOccurred() | ||||
| 			} else { | ||||
| 				touchUpFeedbackGenerator.impactOccurred() | ||||
| 			} | ||||
| 		case .possible, .changed, .failed: | ||||
| 			break | ||||
| 		@unknown default: | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| private class ColorPickerWheelInnerView: UIView { | ||||
| 	private var brightnessView: UIView! | ||||
|  | ||||
| 	var brightness: CGFloat { | ||||
| 		get { 1 - brightnessView.alpha } | ||||
| 		set { brightnessView.alpha = 1 - newValue } | ||||
| 	} | ||||
|  | ||||
| 	var handleLayout: (() -> Void)! | ||||
|  | ||||
| 	private var saturationMask: GradientView! | ||||
|  | ||||
| 	override init(frame: CGRect) { | ||||
| 		super.init(frame: frame) | ||||
|  | ||||
| 		clipsToBounds = true | ||||
|  | ||||
| 		let hueView = GradientView() | ||||
| 		hueView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		hueView.autoresizingMask = [.flexibleWidth, .flexibleHeight] | ||||
| 		hueView.gradientLayer.type = .conic | ||||
| 		hueView.gradientLayer.colors = Color.Component.hue.sliderTintColor(for: Color(red: 1, green: 0, blue: 0, alpha: 1)).map(\.uiColor.cgColor) | ||||
| 		hueView.gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5) | ||||
| 		hueView.gradientLayer.endPoint = CGPoint(x: 0.5, y: 0) | ||||
| 		hueView.gradientLayer.transform = CATransform3DMakeRotation(0.5 * .pi, 0, 0, 1) | ||||
| 		addSubview(hueView) | ||||
|  | ||||
| 		let saturationView = UIView() | ||||
| 		saturationView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		saturationView.autoresizingMask = [.flexibleWidth, .flexibleHeight] | ||||
| 		saturationView.backgroundColor = .white | ||||
| 		addSubview(saturationView) | ||||
|  | ||||
| 		saturationMask = GradientView() | ||||
| 		saturationMask.gradientLayer.type = .radial | ||||
| 		saturationMask.gradientLayer.colors = [UIColor.white.cgColor, UIColor.clear.cgColor] | ||||
| 		saturationMask.gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5) | ||||
| 		saturationMask.gradientLayer.endPoint = CGPoint(x: 1, y: 1) | ||||
| 		saturationView.mask = saturationMask | ||||
|  | ||||
| 		brightnessView = UIView() | ||||
| 		brightnessView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		brightnessView.autoresizingMask = [.flexibleWidth, .flexibleHeight] | ||||
| 		brightnessView.backgroundColor = .black | ||||
| 		addSubview(brightnessView) | ||||
| 	} | ||||
|  | ||||
| 	convenience init() { | ||||
| 		self.init(frame: .zero) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	override func layoutSubviews() { | ||||
| 		super.layoutSubviews() | ||||
| 		layer.cornerRadius = frame.size.height / 2 | ||||
| 		saturationMask.frame = bounds | ||||
| 		handleLayout() | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										403
									
								
								Tweaks/Alderis/Alderis/ColorWell.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,403 @@ | ||||
| // | ||||
| //  ColorWell.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 15/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
| import CoreServices | ||||
|  | ||||
| /// ColorWell can be used to present the user’s color selection in your user interface. It | ||||
| /// optionally also supports drag-and-drop operations. | ||||
| /// | ||||
| /// By default, drop interactions are supported, which causes a `UIControl.Event.valueChanged` event | ||||
| /// to be emitted. Optionally, drag operations can be enabled, allowing the color to be dropped | ||||
| /// elsewhere. | ||||
| /// | ||||
| /// You can also use `UIControl.Event.touchUpInside` to perform an action, such as to initialise | ||||
| /// and present an instance of `ColorPickerViewController`. | ||||
| @objc(HBColorWell) | ||||
| open class ColorWell: UIControl { | ||||
|  | ||||
| 	/// Set the color to be displayed by the view. | ||||
| 	@objc open var color: UIColor? { | ||||
| 		get { colorView.backgroundColor } | ||||
| 		set { colorView.backgroundColor = newValue } | ||||
| 	} | ||||
|  | ||||
| 	/// Override the default border color if desired. | ||||
| 	@objc open var borderColor: UIColor? { | ||||
| 		didSet { updateBorderColor() } | ||||
| 	} | ||||
|  | ||||
| 	/// Whether the user can begin a drag interaction from this view, allowing them to drop the color | ||||
| 	/// into a supporting app. The default is `false`. | ||||
| 	@objc open var isDragInteractionEnabled = false { | ||||
| 		didSet { updateDragDropInteraction() } | ||||
| 	} | ||||
|  | ||||
| 	/// Whether the user can end a drag interaction by dropping on this view, allowing them to drag a | ||||
| 	/// color from a supporting app onto this view. The default is true. | ||||
| 	/// | ||||
| 	/// To handle a color being dropped on this view, add an action for the `.valueChanged` event. For | ||||
| 	/// example: | ||||
| 	/// | ||||
| 	/// ```swift | ||||
| 	/// colorWell.addTarget(self, action: #selector(self.handleColorDidChange(_:)), for: .valueChanged) | ||||
| 	/// ``` | ||||
| 	@objc open var isDropInteractionEnabled = true { | ||||
| 		didSet { updateDragDropInteraction() } | ||||
| 	} | ||||
|  | ||||
| 	#if swift(>=5.3) | ||||
| 	/// Whether the user can long press (iPhone) or right-click (Mac/iPad) the view, allowing them to | ||||
| 	/// copy the color in various formats, or paste a color from another source. | ||||
| 	/// | ||||
| 	/// To handle a color being pasted via the context menu, add an action for the `.valueChanged` | ||||
| 	/// event. For example: | ||||
| 	/// | ||||
| 	/// ```swift | ||||
| 	/// colorWell.addTarget(self, action: #selector(self.handleColorDidChange(_:)), for: .valueChanged) | ||||
| 	/// ``` | ||||
| 	/// | ||||
| 	/// Requires iOS 14 or newer. | ||||
| 	@available(iOS 14, *) | ||||
| 	open override var isContextMenuInteractionEnabled: Bool { | ||||
| 		didSet { updateDragDropInteraction() } | ||||
| 	} | ||||
| 	#endif | ||||
|  | ||||
| 	private var colorView: UIView! | ||||
| 	private var dragInteraction: UIDragInteraction! | ||||
| 	private var dropInteraction: UIDropInteraction! | ||||
| 	private var tapGestureRecognizer: UITapGestureRecognizer! | ||||
|  | ||||
| 	private var contextMenuTitle: String { | ||||
| 		if let color = color { | ||||
| 			return "Color: \(Color(uiColor: color).hexString())" | ||||
| 		} | ||||
| 		return "No color" | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override init(frame: CGRect) { | ||||
| 		super.init(frame: frame) | ||||
| 		setUp() | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	required public init?(coder: NSCoder) { | ||||
| 		super.init(coder: coder) | ||||
| 		setUp() | ||||
| 	} | ||||
|  | ||||
| 	private func setUp() { | ||||
| 		clipsToBounds = true | ||||
| 		backgroundColor = Assets.checkerboardPatternColor | ||||
| 		borderColor = .white | ||||
| 		layer.masksToBounds = false | ||||
| 		layer.shadowColor = UIColor.black.cgColor | ||||
| 		layer.shadowOffset = .zero | ||||
| 		layer.shadowOpacity = 0.75 | ||||
| 		layer.shadowRadius = 1 | ||||
|  | ||||
| 		colorView = UIView() | ||||
| 		colorView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		colorView.clipsToBounds = true | ||||
| 		addSubview(colorView) | ||||
|  | ||||
| 		dragInteraction = UIDragInteraction(delegate: self) | ||||
| 		dragInteraction.isEnabled = true | ||||
| 		dropInteraction = UIDropInteraction(delegate: self) | ||||
| 		updateDragDropInteraction() | ||||
|  | ||||
| 		tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTapGestureRecognizerFired(_:))) | ||||
| 		tapGestureRecognizer.isEnabled = false | ||||
| 		addGestureRecognizer(tapGestureRecognizer) | ||||
|  | ||||
| 		#if swift(>=5.3) | ||||
| 		if #available(iOS 14, *) { | ||||
| 			isContextMenuInteractionEnabled = true | ||||
| 		} | ||||
| 		#endif | ||||
|  | ||||
| 		#if swift(>=5.5) | ||||
| 		if #available(iOS 15, *) { | ||||
| 			toolTip = contextMenuTitle | ||||
| 			toolTipInteraction?.delegate = self | ||||
| 		} | ||||
| 		#endif | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			self.widthAnchor.constraint(greaterThanOrEqualToConstant: UIFloat(32)), | ||||
| 			self.heightAnchor.constraint(equalTo: self.widthAnchor), | ||||
|  | ||||
| 			colorView.leadingAnchor.constraint(equalTo: self.leadingAnchor), | ||||
| 			colorView.trailingAnchor.constraint(equalTo: self.trailingAnchor), | ||||
| 			colorView.topAnchor.constraint(equalTo: self.topAnchor), | ||||
| 			colorView.bottomAnchor.constraint(equalTo: self.bottomAnchor), | ||||
| 		]) | ||||
| 	} | ||||
|  | ||||
| 	private func updateTapGestureRecognizer() { | ||||
| 		let hasTouchUpActions = allControlEvents.contains(.touchUpInside) | ||||
| 		if hasTouchUpActions { | ||||
| 			accessibilityTraits.insert(.button) | ||||
| 		} else { | ||||
| 			accessibilityTraits.remove(.button) | ||||
| 		} | ||||
| 		tapGestureRecognizer.isEnabled = hasTouchUpActions | ||||
| 	} | ||||
|  | ||||
| 	private func updateBorderColor() { | ||||
| 		layer.borderColor = borderColor?.cgColor | ||||
| 	} | ||||
|  | ||||
| 	private func updateDragDropInteraction() { | ||||
| 		isUserInteractionEnabled = isDragInteractionEnabled || isDropInteractionEnabled | ||||
| 		#if swift(>=5.3) | ||||
| 		if #available(iOS 14, *) { | ||||
| 			isUserInteractionEnabled = isUserInteractionEnabled || isContextMenuInteractionEnabled | ||||
| 		} | ||||
| 		#endif | ||||
|  | ||||
| 		if isDragInteractionEnabled { | ||||
| 			addInteraction(dragInteraction) | ||||
| 		} else { | ||||
| 			removeInteraction(dragInteraction) | ||||
| 		} | ||||
|  | ||||
| 		if isDropInteractionEnabled { | ||||
| 			addInteraction(dropInteraction) | ||||
| 		} else { | ||||
| 			removeInteraction(dropInteraction) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func layoutSubviews() { | ||||
| 		super.layoutSubviews() | ||||
|  | ||||
| 		layer.cornerRadius = frame.size.width / 2 | ||||
| 		layer.shadowPath = CGPath(ellipseIn: bounds, transform: nil) | ||||
| 		colorView.layer.cornerRadius = layer.cornerRadius | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func didMoveToWindow() { | ||||
| 		super.didMoveToWindow() | ||||
| 		let scale = window?.screen.scale ?? 1 | ||||
| 		layer.borderWidth = (scale > 2 ? 2 : 1) / scale | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { | ||||
| 		super.traitCollectionDidChange(previousTraitCollection) | ||||
| 		updateBorderColor() | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	open override func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event) { | ||||
| 		super.addTarget(target, action: action, for: controlEvents) | ||||
| 		updateTapGestureRecognizer() | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	@available(iOS 14, macCatalyst 14, *) | ||||
| 	open override func addAction(_ action: UIAction, for controlEvents: UIControl.Event) { | ||||
| 		super.addAction(action, for: controlEvents) | ||||
| 		updateTapGestureRecognizer() | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	open override func removeTarget(_ target: Any?, action: Selector?, for controlEvents: UIControl.Event) { | ||||
| 		super.removeTarget(target, action: action, for: controlEvents) | ||||
| 		updateTapGestureRecognizer() | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	@available(iOS 14, macCatalyst 14, *) | ||||
| 	open override func removeAction(_ action: UIAction, for controlEvents: UIControl.Event) { | ||||
| 		super.removeAction(action, for: controlEvents) | ||||
| 		updateTapGestureRecognizer() | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	@available(iOS 14, macCatalyst 14, *) | ||||
| 	open override func removeAction(identifiedBy actionIdentifier: UIAction.Identifier, for controlEvents: UIControl.Event) { | ||||
| 		super.removeAction(identifiedBy: actionIdentifier, for: controlEvents) | ||||
| 		updateTapGestureRecognizer() | ||||
| 	} | ||||
|  | ||||
| 	@objc private func handleTapGestureRecognizerFired(_ sender: UITapGestureRecognizer) { | ||||
| 		if sender.state == .ended { | ||||
| 			sendActions(for: .touchUpInside) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| /// :nodoc: | ||||
| extension ColorWell { // UIResponder | ||||
|  | ||||
| 	open override var canBecomeFirstResponder: Bool { true } | ||||
|  | ||||
| 	open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { | ||||
| 		switch action { | ||||
| 		case #selector(copy(_:)), #selector(copyHex), #selector(copyRGB), #selector(copyHSL), | ||||
| 				 #selector(copyObjC), #selector(copySwift): | ||||
| 			return color != nil | ||||
|  | ||||
| 		case #selector(paste(_:)): | ||||
| 			return UIPasteboard.general.hasColors || UIPasteboard.general.hasStrings | ||||
|  | ||||
| 		default: | ||||
| 			return super.canPerformAction(action, withSender: sender) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	open override func copy(_ sender: Any?) { | ||||
| 		UIPasteboard.general.color = color | ||||
| 	} | ||||
|  | ||||
| 	open override func paste(_ sender: Any?) { | ||||
| 		if let color = UIPasteboard.general.color ?? UIColor(propertyListValue: UIPasteboard.general.string ?? "") { | ||||
| 			self.color = color | ||||
| 			sendActions(for: .valueChanged) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@objc private func copyHex(_ sender: Any?) { | ||||
| 		UIPasteboard.general.string = Color(uiColor: color!).hexString | ||||
| 	} | ||||
|  | ||||
| 	@objc private func copyRGB(_ sender: Any?) { | ||||
| 		UIPasteboard.general.string = Color(uiColor: color!).rgbString | ||||
| 	} | ||||
|  | ||||
| 	@objc private func copyHSL(_ sender: Any?) { | ||||
| 		UIPasteboard.general.string = Color(uiColor: color!).hslString | ||||
| 	} | ||||
|  | ||||
| 	@objc private func copyObjC(_ sender: Any?) { | ||||
| 		UIPasteboard.general.string = Color(uiColor: color!).objcString | ||||
| 	} | ||||
|  | ||||
| 	@objc private func copySwift(_ sender: Any?) { | ||||
| 		UIPasteboard.general.string = Color(uiColor: color!).swiftString | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| /// :nodoc: | ||||
| extension ColorWell: UIDragInteractionDelegate { | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] { | ||||
| 		guard let color = color else { | ||||
| 			return [] | ||||
| 		} | ||||
| 		let provider = NSItemProvider(object: color) | ||||
| 		let item = UIDragItem(itemProvider: provider) | ||||
| 		item.localObject = color | ||||
| 		return [item] | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| /// :nodoc: | ||||
| extension ColorWell: UIDropInteractionDelegate { | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool { | ||||
| 		return session.items.count == 1 && session.canLoadObjects(ofClass: UIColor.self) | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal { | ||||
| 		return UIDropProposal(operation: .copy) | ||||
| 	} | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) { | ||||
| 		session.loadObjects(ofClass: UIColor.self) { items in | ||||
| 			if let color = items.first as? UIColor { | ||||
| 				self.color = color | ||||
| 				self.sendActions(for: .valueChanged) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| #if swift(>=5.5) | ||||
| /// :nodoc: | ||||
| @available(iOS 15, *) | ||||
| extension ColorWell: UIToolTipInteractionDelegate { | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	public func toolTipInteraction(_ interaction: UIToolTipInteraction, configurationAt point: CGPoint) -> UIToolTipConfiguration? { | ||||
| 		UIToolTipConfiguration(toolTip: contextMenuTitle) | ||||
| 	} | ||||
|  | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #if swift(>=5.3) | ||||
| /// :nodoc: | ||||
| @available(iOS 13, *) | ||||
| extension ColorWell { // UIContextMenuInteractionDelegate | ||||
|  | ||||
| 	/// :nodoc: | ||||
| 	open override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { | ||||
| 		return UIContextMenuConfiguration(identifier: color, previewProvider: nil) { items in | ||||
| 			var children = [UIMenuElement]() | ||||
| 			if isCatalyst { | ||||
| 				children += [ | ||||
| 					UIMenu(title: "", options: .displayInline, children: [ | ||||
| 						UICommand(title: self.contextMenuTitle, | ||||
| 											action: #selector(self.doesNotRecognizeSelector(_:)), | ||||
| 											attributes: .disabled) | ||||
| 					]) | ||||
| 				] | ||||
| 			} | ||||
| 			children += items | ||||
|  | ||||
| 			var objcImageName = "chevron.left.slash.chevron.right" | ||||
| 			var swiftImageName = "chevron.left.slash.chevron.right" | ||||
| 			if #available(iOS 14, *) { | ||||
| 				objcImageName = "curlybraces" | ||||
| 				swiftImageName = "swift" | ||||
| 			} | ||||
| 			children += [ | ||||
| 				UIMenu(title: "", options: .displayInline, children: [ | ||||
| 					UICommand(title: "Copy as Hex", | ||||
| 										image: UIImage(systemName: "number"), | ||||
| 										action: #selector(self.copyHex(_:))), | ||||
| 					UICommand(title: "Copy as RGB", | ||||
| 										image: UIImage(systemName: "r.circle"), | ||||
| 										action: #selector(self.copyRGB(_:))), | ||||
| 					UICommand(title: "Copy as HSL", | ||||
| 										image: UIImage(systemName: "h.circle"), | ||||
| 										action: #selector(self.copyHSL(_:))), | ||||
| 					UICommand(title: "Copy as Objective-C", | ||||
| 										image: UIImage(systemName: objcImageName), | ||||
| 										action: #selector(self.copyObjC(_:))), | ||||
| 					UICommand(title: "Copy as Swift", | ||||
| 										image: UIImage(systemName: swiftImageName), | ||||
| 										action: #selector(self.copySwift(_:))), | ||||
| 				]) | ||||
| 			] | ||||
| 			return UIMenu(title: self.contextMenuTitle, children: children) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /// Deprecated. Use `ColorWell` instead. | ||||
| @available(*, deprecated, renamed: "ColorWell") | ||||
| @objc(HBColorPickerCircleView) | ||||
| open class ColorPickerCircleView: ColorWell {} | ||||
							
								
								
									
										34
									
								
								Tweaks/Alderis/Alderis/DialogButton.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | ||||
| // | ||||
| //  DialogButton.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 28/9/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class DialogButton: UIButton { | ||||
|  | ||||
| 	var highlightBackgroundColor: UIColor? | ||||
|  | ||||
| 	init() { | ||||
| 		super.init(frame: .zero) | ||||
|  | ||||
| 		addTarget(self, action: #selector(self.handleTouchDown), for: [.touchDown, .touchDragEnter]) | ||||
| 		addTarget(self, action: #selector(self.handleTouchUp), for: [.touchUpInside, .touchUpOutside, .touchDragExit, .touchCancel]) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	@objc private func handleTouchDown() { | ||||
| 		backgroundColor = highlightBackgroundColor | ||||
| 	} | ||||
|  | ||||
| 	@objc private func handleTouchUp() { | ||||
| 		backgroundColor = nil | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										18
									
								
								Tweaks/Alderis/Alderis/GradientView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,18 @@ | ||||
| // | ||||
| //  GradientView.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 11/5/2022. | ||||
| //  Copyright © 2022 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class GradientView: UIView { | ||||
|  | ||||
| 	override class var layerClass: AnyClass { CAGradientLayer.self } | ||||
|  | ||||
| 	// swiftlint:disable:next force_cast | ||||
| 	var gradientLayer: CAGradientLayer { layer as! CAGradientLayer } | ||||
|  | ||||
| } | ||||
							
								
								
									
										22
									
								
								Tweaks/Alderis/Alderis/Info.plist
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>CFBundleDevelopmentRegion</key> | ||||
| 	<string>$(DEVELOPMENT_LANGUAGE)</string> | ||||
| 	<key>CFBundleExecutable</key> | ||||
| 	<string>$(EXECUTABLE_NAME)</string> | ||||
| 	<key>CFBundleIdentifier</key> | ||||
| 	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||||
| 	<key>CFBundleInfoDictionaryVersion</key> | ||||
| 	<string>6.0</string> | ||||
| 	<key>CFBundleName</key> | ||||
| 	<string>$(PRODUCT_NAME)</string> | ||||
| 	<key>CFBundlePackageType</key> | ||||
| 	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||||
| 	<key>CFBundleShortVersionString</key> | ||||
| 	<string>$(MARKETING_VERSION)</string> | ||||
| 	<key>CFBundleVersion</key> | ||||
| 	<string>$(CURRENT_PROJECT_VERSION)</string> | ||||
| </dict> | ||||
| </plist> | ||||
							
								
								
									
										29
									
								
								Tweaks/Alderis/Alderis/NSBeep.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,29 @@ | ||||
| // | ||||
| //  NSBeep.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 8/5/2022. | ||||
| //  Copyright © 2022 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal typealias NSBeepType = @convention(c) () -> Void | ||||
|  | ||||
| internal let NSBeep: NSBeepType? = { | ||||
| 	if isCatalyst, | ||||
| 		 let appkit = dlopen("/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit", RTLD_LAZY), | ||||
| 		 let beep = dlsym(appkit, "NSBeep") { | ||||
| 		return unsafeBitCast(beep, to: NSBeepType.self) | ||||
| 	} | ||||
| 	return nil | ||||
| }() | ||||
|  | ||||
| internal func beep() { | ||||
| 	if isCatalyst { | ||||
| 		NSBeep?() | ||||
| 	} else { | ||||
| 		let feedbackGenerator = UINotificationFeedbackGenerator() | ||||
| 		feedbackGenerator.notificationOccurred(.error) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										60
									
								
								Tweaks/Alderis/Alderis/SeparatorView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,60 @@ | ||||
| // | ||||
| //  SeparatorView.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 12/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal class SeparatorView: UIView { | ||||
|  | ||||
| 	enum Direction { | ||||
| 		case horizontal, vertical | ||||
| 	} | ||||
|  | ||||
| 	var direction: Direction { | ||||
| 		didSet { updateConstraints() } | ||||
| 	} | ||||
|  | ||||
| 	private var widthConstraint: NSLayoutConstraint! | ||||
| 	private var heightConstraint: NSLayoutConstraint! | ||||
|  | ||||
| 	init(direction: Direction) { | ||||
| 		self.direction = direction | ||||
| 		super.init(frame: .zero) | ||||
|  | ||||
| 		backgroundColor = Assets.separatorColor | ||||
|  | ||||
| 		widthConstraint = widthAnchor.constraint(equalToConstant: 1) | ||||
| 		heightConstraint = heightAnchor.constraint(equalToConstant: 1) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	override func updateConstraints() { | ||||
| 		super.updateConstraints() | ||||
|  | ||||
| 		let constant = 1 / (window?.screen.scale ?? 1) | ||||
|  | ||||
| 		switch direction { | ||||
| 		case .horizontal: | ||||
| 			widthConstraint.isActive = false | ||||
| 			heightConstraint.isActive = true | ||||
| 			heightConstraint.constant = constant | ||||
| 		case .vertical: | ||||
| 			widthConstraint.isActive = true | ||||
| 			heightConstraint.isActive = false | ||||
| 			widthConstraint.constant = constant | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	override func didMoveToWindow() { | ||||
| 		super.didMoveToWindow() | ||||
| 		updateConstraints() | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										86
									
								
								Tweaks/Alderis/Alderis/TextViewLabel.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,86 @@ | ||||
| // | ||||
| //  TextViewLabel.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 8/5/2022. | ||||
| //  Copyright © 2022 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
| import SafariServices | ||||
|  | ||||
| internal class TextViewLabel: UITextView { | ||||
|  | ||||
| 	override init(frame: CGRect, textContainer: NSTextContainer?) { | ||||
| 		super.init(frame: frame, textContainer: textContainer) | ||||
|  | ||||
| 		delegate = self | ||||
| 		backgroundColor = nil | ||||
| 		textContainerInset = .zero | ||||
| 		self.textContainer.lineFragmentPadding = 0 | ||||
| 		isEditable = false | ||||
| 		isScrollEnabled = false | ||||
| 		adjustsFontForContentSizeCategory = true | ||||
| 	} | ||||
|  | ||||
| 	convenience init() { | ||||
| 		self.init(frame: .zero, textContainer: nil) | ||||
| 	} | ||||
|  | ||||
| 	required init?(coder: NSCoder) { | ||||
| 		fatalError("init(coder:) has not been implemented") | ||||
| 	} | ||||
|  | ||||
| 	override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { | ||||
| 		// 🧡 to https://stackoverflow.com/a/44878203/709376 for this | ||||
| 		guard super.point(inside: point, with: event), | ||||
| 					let position = closestPosition(to: point), | ||||
| 					let range = tokenizer.rangeEnclosingPosition(position, | ||||
| 																											 with: .character, | ||||
| 																											 inDirection: .layout(.left)) else { | ||||
| 			return false | ||||
| 		} | ||||
| 		let index = offset(from: beginningOfDocument, to: range.start) | ||||
| 		return attributedText.attribute(.link, at: index, effectiveRange: nil) != nil | ||||
| 	} | ||||
|  | ||||
| 	override var selectedRange: NSRange { | ||||
| 		get { NSRange() } | ||||
| 		set {} | ||||
| 	} | ||||
|  | ||||
| 	private var viewController: UIViewController? { | ||||
| 		var responder: UIResponder? = self | ||||
| 		while responder != nil { | ||||
| 			if let viewController = responder as? UIViewController { | ||||
| 				return viewController | ||||
| 			} | ||||
| 			responder = responder?.next | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| extension TextViewLabel: UITextViewDelegate { | ||||
|  | ||||
| 	func textView(_ textView: UITextView, shouldInteractWith url: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { | ||||
| 		#if targetEnvironment(macCatalyst) | ||||
| 		// No need to do anything custom. | ||||
| 		return true | ||||
| 		#else | ||||
| 		switch interaction { | ||||
| 		case .invokeDefaultAction: | ||||
| 			let safariViewController = SFSafariViewController(url: url) | ||||
| 			safariViewController.modalPresentationStyle = .formSheet | ||||
| 			viewController?.present(safariViewController, animated: true) | ||||
| 			return false | ||||
| 		case .presentActions, .preview: | ||||
| 			return true | ||||
| 		@unknown default: | ||||
| 			return true | ||||
| 		} | ||||
| 		#endif | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										199
									
								
								Tweaks/Alderis/Alderis/UIColorAdditions.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,199 @@ | ||||
| // | ||||
| //  UIColorAdditions.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Ryan Nair on 10/5/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| /// ColorPropertyListValue is a protocol representing types that can be passed to the\ | ||||
| /// `UIColor.init(propertyListValue:)` initialiser. `String` and `Array` both conform to this type. | ||||
| /// | ||||
| /// - see: `UIColor.init(propertyListValue:)` | ||||
| public protocol ColorPropertyListValue {} | ||||
|  | ||||
| /// A string can represent a `ColorPropertyListValue`. | ||||
| /// | ||||
| /// - see: `UIColor.init(propertyListValue:)` | ||||
| extension String: ColorPropertyListValue {} | ||||
|  | ||||
| /// An array of integers can represent a `ColorPropertyListValue`. | ||||
| /// | ||||
| /// - see: `UIColor.init(propertyListValue:)` | ||||
| extension Array: ColorPropertyListValue where Element: FixedWidthInteger {} | ||||
|  | ||||
| /// Alderis provides extensions to `UIColor` for the purpose of serializing and deserializing colors | ||||
| /// into representations that can be stored in property lists, JSON, and similar formats. | ||||
| public extension UIColor { | ||||
|  | ||||
| 	/// Initializes and returns a color object using data from the specified object. | ||||
| 	/// | ||||
| 	/// The value is expected to be one of: | ||||
| 	/// | ||||
| 	/// * An array of 3 or 4 integer RGB or RGBA color components, with values between 0 and 255 (e.g. | ||||
| 	///   `[218, 192, 222]`) | ||||
| 	/// * A CSS-style hex string, with an optional alpha component (e.g. `#DAC0DE` or `#DACODE55`) | ||||
| 	/// * A short CSS-style hex string, with an optional alpha component (e.g. `#DC0` or `#DC05`) | ||||
| 	/// | ||||
| 	/// Use `-[UIColor initWithHbcp_propertyListValue:]` to access this method from Objective-C. | ||||
| 	/// | ||||
| 	/// - parameter value: The object to retrieve data from. See the discussion for the supported object | ||||
| 	/// types. | ||||
| 	/// - returns: An initialized color object, or nil if the value does not conform to the expected | ||||
| 	/// type. The color information represented by this object is in the device RGB colorspace. | ||||
| 	/// - see: `propertyListValue` | ||||
| 	@nonobjc convenience init?(propertyListValue: ColorPropertyListValue?) { | ||||
| 		if let array = propertyListValue as? [Int], array.count == 3 || array.count == 4 { | ||||
| 			let floats = array.map(CGFloat.init(_:)) | ||||
| 			self.init(red: floats[0] / 255, | ||||
| 								green: floats[1] / 255, | ||||
| 								blue: floats[2] / 255, | ||||
| 								alpha: array.count == 4 ? floats[3] : 1) | ||||
| 			return | ||||
| 		} else if var string = propertyListValue as? String { | ||||
| 			if string.count == 4 || string.count == 5 { | ||||
| 				let r = String(repeating: string[string.index(string.startIndex, offsetBy: 1)], count: 2) | ||||
| 				let g = String(repeating: string[string.index(string.startIndex, offsetBy: 2)], count: 2) | ||||
| 				let b = String(repeating: string[string.index(string.startIndex, offsetBy: 3)], count: 2) | ||||
| 				let a = string.count == 5 ? String(repeating: string[string.index(string.startIndex, offsetBy: 4)], count: 2) : "FF" | ||||
| 				string = r + g + b + a | ||||
| 			} | ||||
|  | ||||
| 			var hex: UInt64 = 0 | ||||
| 			let scanner = Scanner(string: string) | ||||
| 			guard scanner.scanString("#", into: nil), | ||||
| 						scanner.scanHexInt64(&hex) else { | ||||
| 				return nil | ||||
| 			} | ||||
|  | ||||
| 			if string.count == 9 { | ||||
| 				self.init(red: CGFloat((hex & 0xFF000000) >> 24) / 255, | ||||
| 									green: CGFloat((hex & 0x00FF0000) >> 16) / 255, | ||||
| 									blue: CGFloat((hex & 0x0000FF00) >> 8) / 255, | ||||
| 									alpha: CGFloat((hex & 0x000000FF) >> 0) / 255) | ||||
| 				return | ||||
| 			} else { | ||||
| 				var alpha: Float = 1 | ||||
| 				if scanner.scanString(":", into: nil) { | ||||
| 					// Continue scanning to get the alpha component. | ||||
| 					scanner.scanFloat(&alpha) | ||||
| 				} | ||||
|  | ||||
| 				self.init(red: CGFloat((hex & 0xFF0000) >> 16) / 255, | ||||
| 									green: CGFloat((hex & 0x00FF00) >> 8) / 255, | ||||
| 									blue: CGFloat((hex & 0x0000FF) >> 0) / 255, | ||||
| 									alpha: CGFloat(alpha)) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	/// Maps `init(propertyListValue:)` to Objective-C due to Swift limitations. This is an | ||||
| 	/// implementation detail. Ignore this and use `UIColor(propertyListValue:)` or | ||||
| 	/// `-[UIColor initWithHbcp_propertyListValue:]` as per usual. | ||||
| 	/// | ||||
| 	/// - parameter value: The object to retrieve data from. See the discussion for the supported | ||||
| 	/// object types. | ||||
| 	/// - returns: An initialized color object, or nil if the value does not conform to the expected | ||||
| 	/// type. The color information represented by this object is in the device RGB colorspace. | ||||
| 	/// device RGB colorspace. | ||||
| 	/// - see: `init(propertyListValue:)` | ||||
| 	@objc(initWithHbcp_propertyListValue:) | ||||
| 	convenience init?(_propertyListValueObjC propertyListValue: Any?) { | ||||
| 		if let value = propertyListValue as? String { | ||||
| 			self.init(propertyListValue: value) | ||||
| 		} else if let value = propertyListValue as? [Int] { | ||||
| 			self.init(propertyListValue: value) | ||||
| 		} else { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/// Returns a string that represents the color. | ||||
| 	/// | ||||
| 	/// The output is a string in the format `#AABBCC:0.5`, where the initial `#AABBCC` portion is a | ||||
| 	/// 6-character CSS-style hex string, and the final `:0.5` portion represents the alpha value. If | ||||
| 	/// the color’s alpha value is `1`, the alpha portion is excluded. | ||||
| 	/// | ||||
| 	/// If the color is dynamic, for instance if it is a UIKit system color, or initialised via | ||||
| 	/// `UIColor(dynamicProvider:)`, the color that matches the current trait collection is used. | ||||
| 	/// | ||||
| 	/// - returns: A string representing the color, in the format discussed above. | ||||
| 	/// - see: `init(propertyListValue:)` | ||||
| 	@objc(hbcp_propertyListValue) | ||||
| 	var propertyListValue: String { | ||||
| 		var alpha: CGFloat = 0 | ||||
| 		getRed(nil, green: nil, blue: nil, alpha: &alpha) | ||||
|  | ||||
| 		let color = Color(uiColor: self.withAlphaComponent(1)) | ||||
| 		let hexString = color.hexString() | ||||
| 		let alphaString = alpha == 1 ? "" : String(format: ":%.5G", alpha) | ||||
| 		return "\(hexString)\(alphaString)" | ||||
| 	} | ||||
|  | ||||
| 	/// Returns a hexadecimal string that represents the color. | ||||
| 	/// | ||||
| 	/// The output is a string in the format `#AABBCCDD`, where the initial `#AABBCC` portion is a | ||||
| 	/// 6-character CSS-style hex string, and the final `DD` portion is the hexadecimal representation | ||||
| 	/// of the alpha value, supported by [recent web browsers](https://caniuse.com/css-rrggbbaa). | ||||
| 	/// | ||||
| 	/// If the color is dynamic, for instance if it is a UIKit system color, or initialised via | ||||
| 	/// `UIColor(dynamicProvider:)`, the color that matches the current trait collection is used. | ||||
| 	/// | ||||
| 	/// - returns: A string representing the color, in the format discussed above. | ||||
| 	@objc(hbcp_hexString) | ||||
| 	var hexString: String { Color(uiColor: self).hexString } | ||||
|  | ||||
| 	/// Returns an RGB string that represents the color. | ||||
| 	/// | ||||
| 	/// The output is a string in the format of `rgba(170, 187, 204, 0.5)`, or `rgb(170, 187, 204)` if | ||||
| 	/// the color’s alpha value is `1`. | ||||
| 	/// | ||||
| 	/// If the color is dynamic, for instance if it is a UIKit system color, or initialised via | ||||
| 	/// `UIColor(dynamicProvider:)`, the color that matches the current trait collection is used. | ||||
| 	/// | ||||
| 	/// - returns: A string representing the color, in the format discussed above. | ||||
| 	@objc(hbcp_rgbString) | ||||
| 	var rgbString: String { Color(uiColor: self).rgbString } | ||||
|  | ||||
| 	/// Returns an HSL string that represents the color. | ||||
| 	/// | ||||
| 	/// The output is a string in the format of `hsla(170, 187, 204, 0.5)`, or `hsl(170, 187, 204)` if | ||||
| 	/// the color’s alpha value is `1`. | ||||
| 	/// | ||||
| 	/// If the color is dynamic, for instance if it is a UIKit system color, or initialised via | ||||
| 	/// `UIColor(dynamicProvider:)`, the color that matches the current trait collection is used. | ||||
| 	/// | ||||
| 	/// - returns: A string representing the color, in the format discussed above. | ||||
| 	@objc(hbcp_hslString) | ||||
| 	var hslString: String { Color(uiColor: self).hslString } | ||||
|  | ||||
| 	/// Returns an Objective-C UIColor string that represents the color. | ||||
| 	/// | ||||
| 	/// The output is a string in the format of | ||||
| 	/// `[UIColor colorWithRed:0.667 green:0.733 blue:0.800 alpha:1.00]`. | ||||
| 	/// | ||||
| 	/// If the color is dynamic, for instance if it is a UIKit system color, or initialised via | ||||
| 	/// `UIColor(dynamicProvider:)`, the color that matches the current trait collection is used. | ||||
| 	/// | ||||
| 	/// - returns: A string representing the color, in the format discussed above. | ||||
| 	@objc(hbcp_objcString) | ||||
| 	var objcString: String { Color(uiColor: self).objcString } | ||||
|  | ||||
| 	/// Returns a Swift UIColor string that represents the color. | ||||
| 	/// | ||||
| 	/// The output is a string in the format of | ||||
| 	/// `UIColor(red: 0.667, green: 0.733, blue: 0.800, alpha: 1.00)`. | ||||
| 	/// | ||||
| 	/// If the color is dynamic, for instance if it is a UIKit system color, or initialised via | ||||
| 	/// `UIColor(dynamicProvider:)`, the color that matches the current trait collection is used. | ||||
| 	/// | ||||
| 	/// - returns: A string representing the color, in the format discussed above. | ||||
| 	@objc(hbcp_swiftString) | ||||
| 	var swiftString: String { Color(uiColor: self).swiftString } | ||||
|  | ||||
| } | ||||
							
								
								
									
										34
									
								
								Tweaks/Alderis/Alderis/UIFloat.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | ||||
| // | ||||
| //  UIFloat.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 8/5/2022. | ||||
| //  Copyright © 2022 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal let isCatalyst: Bool = { | ||||
| 	if #available(iOS 13, *) { | ||||
| 		return ProcessInfo.processInfo.isMacCatalystApp | ||||
| 	} | ||||
| 	return false | ||||
| }() | ||||
|  | ||||
| // Catalyst iPad mode, with iOS UI at 0.77 scale | ||||
| internal let isCatalystPad = isCatalyst && UIDevice.current.userInterfaceIdiom == .pad | ||||
|  | ||||
| // Catalyst Mac mode, with Mac UI at 1.00 scale | ||||
| internal let isCatalystMac: Bool = { | ||||
| 	#if swift(>=5.3) | ||||
| 	if #available(iOS 14, *) { | ||||
| 		return UIDevice.current.userInterfaceIdiom == .mac | ||||
| 	} | ||||
| 	#endif | ||||
| 	return false | ||||
| }() | ||||
|  | ||||
| // Inspired by https://www.highcaffeinecontent.com/blog/20220216-Where-Mac-Catalyst-Falls-Short | ||||
| internal func UIFloat(_ value: CGFloat) -> CGFloat { | ||||
| 	return floor(value * (isCatalystMac ? 0.77 : 1)) | ||||
| } | ||||
							
								
								
									
										32
									
								
								Tweaks/Alderis/Alderis/UIFontDescriptorAdditions.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,32 @@ | ||||
| // | ||||
| //  UIFontDescriptorAdditions.swift | ||||
| //  Alderis | ||||
| // | ||||
| //  Created by Adam Demasi on 7/5/2022. | ||||
| //  Copyright © 2022 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| internal extension UIFontDescriptor.FeatureKey { | ||||
|  | ||||
| 	// Abstracts a messy API change made in iOS 15. | ||||
| 	static var alderisFeature: Self { | ||||
| 		#if swift(>=5.5) | ||||
| 		if #available(iOS 15, *) { | ||||
| 			return .type | ||||
| 		} | ||||
| 		#endif | ||||
| 		return .featureIdentifier | ||||
| 	} | ||||
|  | ||||
| 	static var alderisSelector: Self { | ||||
| 		#if swift(>=5.5) | ||||
| 		if #available(iOS 15, *) { | ||||
| 			return .selector | ||||
| 		} | ||||
| 		#endif | ||||
| 		return .typeIdentifier | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										444
									
								
								Tweaks/Alderis/Demo/Alderis Demo.xcodeproj/project.pbxproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,444 @@ | ||||
| // !$*UTF8*$! | ||||
| { | ||||
| 	archiveVersion = 1; | ||||
| 	classes = { | ||||
| 	}; | ||||
| 	objectVersion = 51; | ||||
| 	objects = { | ||||
|  | ||||
| /* Begin PBXBuildFile section */ | ||||
| 		129B4F59BA5C9D60F07CE7E0 /* libPods-Alderis Demo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ABFF0FA1430DE1F1C311810B /* libPods-Alderis Demo.a */; }; | ||||
| 		CF39D44B222BC07E001EF57F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CF39D44A222BC07E001EF57F /* Assets.xcassets */; }; | ||||
| 		CF6B3B9E2446A13C000A608B /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6B3B9D2446A13C000A608B /* FirstViewController.swift */; }; | ||||
| 		CF83082423F7819A00157D3F /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CF83082323F7819A00157D3F /* Launch Screen.storyboard */; }; | ||||
| 		CFE70278241A593200083903 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFE70277241A593200083903 /* AppDelegate.swift */; }; | ||||
| /* End PBXBuildFile section */ | ||||
|  | ||||
| /* Begin PBXCopyFilesBuildPhase section */ | ||||
| 		CF73D33E241F9C23000B1B10 /* Embed Frameworks */ = { | ||||
| 			isa = PBXCopyFilesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			dstPath = ""; | ||||
| 			dstSubfolderSpec = 10; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			name = "Embed Frameworks"; | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXCopyFilesBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXFileReference section */ | ||||
| 		2B5DB4BF0F331C22D5DFB481 /* Pods-Alderis Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Alderis Demo.release.xcconfig"; path = "Target Support Files/Pods-Alderis Demo/Pods-Alderis Demo.release.xcconfig"; sourceTree = "<group>"; }; | ||||
| 		9FD89BFF752E9D684C11E887 /* Pods-Alderis Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Alderis Demo.debug.xcconfig"; path = "Target Support Files/Pods-Alderis Demo/Pods-Alderis Demo.debug.xcconfig"; sourceTree = "<group>"; }; | ||||
| 		ABFF0FA1430DE1F1C311810B /* libPods-Alderis Demo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Alderis Demo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| 		CF39D43E222BC07C001EF57F /* Alderis Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Alderis Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| 		CF39D44A222BC07E001EF57F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | ||||
| 		CF39D44F222BC07E001EF57F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||||
| 		CF6B3B9D2446A13C000A608B /* FirstViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = "<group>"; }; | ||||
| 		CF83082323F7819A00157D3F /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; }; | ||||
| 		CFCF774D242113EE00379864 /* AlderisDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AlderisDemo.entitlements; sourceTree = "<group>"; }; | ||||
| 		CFE70277241A593200083903 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | ||||
| /* End PBXFileReference section */ | ||||
|  | ||||
| /* Begin PBXFrameworksBuildPhase section */ | ||||
| 		CF39D43B222BC07C001EF57F /* Frameworks */ = { | ||||
| 			isa = PBXFrameworksBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				129B4F59BA5C9D60F07CE7E0 /* libPods-Alderis Demo.a in Frameworks */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXFrameworksBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXGroup section */ | ||||
| 		13755DEE92D2481708433C7E /* Frameworks */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				ABFF0FA1430DE1F1C311810B /* libPods-Alderis Demo.a */, | ||||
| 			); | ||||
| 			name = Frameworks; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		AB49FDA0EE3B301C0383D6FF /* Pods */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				9FD89BFF752E9D684C11E887 /* Pods-Alderis Demo.debug.xcconfig */, | ||||
| 				2B5DB4BF0F331C22D5DFB481 /* Pods-Alderis Demo.release.xcconfig */, | ||||
| 			); | ||||
| 			path = Pods; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		CF39D435222BC07C001EF57F = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				CF39D440222BC07C001EF57F /* Alderis Demo */, | ||||
| 				CF39D43F222BC07C001EF57F /* Products */, | ||||
| 				AB49FDA0EE3B301C0383D6FF /* Pods */, | ||||
| 				13755DEE92D2481708433C7E /* Frameworks */, | ||||
| 			); | ||||
| 			indentWidth = 2; | ||||
| 			sourceTree = "<group>"; | ||||
| 			tabWidth = 2; | ||||
| 			usesTabs = 1; | ||||
| 		}; | ||||
| 		CF39D43F222BC07C001EF57F /* Products */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				CF39D43E222BC07C001EF57F /* Alderis Demo.app */, | ||||
| 			); | ||||
| 			name = Products; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		CF39D440222BC07C001EF57F /* Alderis Demo */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				CFCF774D242113EE00379864 /* AlderisDemo.entitlements */, | ||||
| 				CF83082323F7819A00157D3F /* Launch Screen.storyboard */, | ||||
| 				CFE70277241A593200083903 /* AppDelegate.swift */, | ||||
| 				CF6B3B9D2446A13C000A608B /* FirstViewController.swift */, | ||||
| 				CF39D44A222BC07E001EF57F /* Assets.xcassets */, | ||||
| 				CF39D44F222BC07E001EF57F /* Info.plist */, | ||||
| 			); | ||||
| 			path = "Alderis Demo"; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| /* End PBXGroup section */ | ||||
|  | ||||
| /* Begin PBXNativeTarget section */ | ||||
| 		CF39D43D222BC07C001EF57F /* Alderis Demo */ = { | ||||
| 			isa = PBXNativeTarget; | ||||
| 			buildConfigurationList = CF39D454222BC07E001EF57F /* Build configuration list for PBXNativeTarget "Alderis Demo" */; | ||||
| 			buildPhases = ( | ||||
| 				B02FB21B4CF370106F8C5ADD /* [CP] Check Pods Manifest.lock */, | ||||
| 				CF39D43A222BC07C001EF57F /* Sources */, | ||||
| 				CF39D43B222BC07C001EF57F /* Frameworks */, | ||||
| 				CF39D43C222BC07C001EF57F /* Resources */, | ||||
| 				CF73D33E241F9C23000B1B10 /* Embed Frameworks */, | ||||
| 				CF79DA2F25171E2D00F17BCB /* SwiftLint */, | ||||
| 				638C9DB382A92024C20F1301 /* [CP] Copy Pods Resources */, | ||||
| 			); | ||||
| 			buildRules = ( | ||||
| 			); | ||||
| 			dependencies = ( | ||||
| 			); | ||||
| 			name = "Alderis Demo"; | ||||
| 			productName = "Alderis Demo"; | ||||
| 			productReference = CF39D43E222BC07C001EF57F /* Alderis Demo.app */; | ||||
| 			productType = "com.apple.product-type.application"; | ||||
| 		}; | ||||
| /* End PBXNativeTarget section */ | ||||
|  | ||||
| /* Begin PBXProject section */ | ||||
| 		CF39D436222BC07C001EF57F /* Project object */ = { | ||||
| 			isa = PBXProject; | ||||
| 			attributes = { | ||||
| 				LastUpgradeCheck = 1330; | ||||
| 				ORGANIZATIONNAME = "HASHBANG Productions"; | ||||
| 				TargetAttributes = { | ||||
| 					CF39D43D222BC07C001EF57F = { | ||||
| 						CreatedOnToolsVersion = 10.0; | ||||
| 						LastSwiftMigration = 1130; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 			buildConfigurationList = CF39D439222BC07C001EF57F /* Build configuration list for PBXProject "Alderis Demo" */; | ||||
| 			compatibilityVersion = "Xcode 9.3"; | ||||
| 			developmentRegion = en; | ||||
| 			hasScannedForEncodings = 0; | ||||
| 			knownRegions = ( | ||||
| 				en, | ||||
| 				Base, | ||||
| 			); | ||||
| 			mainGroup = CF39D435222BC07C001EF57F; | ||||
| 			productRefGroup = CF39D43F222BC07C001EF57F /* Products */; | ||||
| 			projectDirPath = ""; | ||||
| 			projectRoot = ""; | ||||
| 			targets = ( | ||||
| 				CF39D43D222BC07C001EF57F /* Alderis Demo */, | ||||
| 			); | ||||
| 		}; | ||||
| /* End PBXProject section */ | ||||
|  | ||||
| /* Begin PBXResourcesBuildPhase section */ | ||||
| 		CF39D43C222BC07C001EF57F /* Resources */ = { | ||||
| 			isa = PBXResourcesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				CF83082423F7819A00157D3F /* Launch Screen.storyboard in Resources */, | ||||
| 				CF39D44B222BC07E001EF57F /* Assets.xcassets in Resources */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXResourcesBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXShellScriptBuildPhase section */ | ||||
| 		638C9DB382A92024C20F1301 /* [CP] Copy Pods Resources */ = { | ||||
| 			isa = PBXShellScriptBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			inputFileListPaths = ( | ||||
| 				"${PODS_ROOT}/Target Support Files/Pods-Alderis Demo/Pods-Alderis Demo-resources-${CONFIGURATION}-input-files.xcfilelist", | ||||
| 			); | ||||
| 			name = "[CP] Copy Pods Resources"; | ||||
| 			outputFileListPaths = ( | ||||
| 				"${PODS_ROOT}/Target Support Files/Pods-Alderis Demo/Pods-Alderis Demo-resources-${CONFIGURATION}-output-files.xcfilelist", | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 			shellPath = /bin/sh; | ||||
| 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Alderis Demo/Pods-Alderis Demo-resources.sh\"\n"; | ||||
| 			showEnvVarsInLog = 0; | ||||
| 		}; | ||||
| 		B02FB21B4CF370106F8C5ADD /* [CP] Check Pods Manifest.lock */ = { | ||||
| 			isa = PBXShellScriptBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			inputFileListPaths = ( | ||||
| 			); | ||||
| 			inputPaths = ( | ||||
| 				"${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||||
| 				"${PODS_ROOT}/Manifest.lock", | ||||
| 			); | ||||
| 			name = "[CP] Check Pods Manifest.lock"; | ||||
| 			outputFileListPaths = ( | ||||
| 			); | ||||
| 			outputPaths = ( | ||||
| 				"$(DERIVED_FILE_DIR)/Pods-Alderis Demo-checkManifestLockResult.txt", | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 			shellPath = /bin/sh; | ||||
| 			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"; | ||||
| 			showEnvVarsInLog = 0; | ||||
| 		}; | ||||
| 		CF79DA2F25171E2D00F17BCB /* SwiftLint */ = { | ||||
| 			isa = PBXShellScriptBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			inputFileListPaths = ( | ||||
| 			); | ||||
| 			inputPaths = ( | ||||
| 			); | ||||
| 			name = SwiftLint; | ||||
| 			outputFileListPaths = ( | ||||
| 			); | ||||
| 			outputPaths = ( | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 			shellPath = /bin/sh; | ||||
| 			shellScript = "if which swiftlint >/dev/null; then\n\tswiftlint\nelse\n\techo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; | ||||
| 		}; | ||||
| /* End PBXShellScriptBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXSourcesBuildPhase section */ | ||||
| 		CF39D43A222BC07C001EF57F /* Sources */ = { | ||||
| 			isa = PBXSourcesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				CFE70278241A593200083903 /* AppDelegate.swift in Sources */, | ||||
| 				CF6B3B9E2446A13C000A608B /* FirstViewController.swift in Sources */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXSourcesBuildPhase section */ | ||||
|  | ||||
| /* Begin XCBuildConfiguration section */ | ||||
| 		CF39D452222BC07E001EF57F /* Debug */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | ||||
| 				CLANG_ENABLE_OBJC_WEAK = YES; | ||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||
| 				CLANG_WARN_BOOL_CONVERSION = YES; | ||||
| 				CLANG_WARN_COMMA = YES; | ||||
| 				CLANG_WARN_CONSTANT_CONVERSION = YES; | ||||
| 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||||
| 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||||
| 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||||
| 				CLANG_WARN_EMPTY_BODY = YES; | ||||
| 				CLANG_WARN_ENUM_CONVERSION = YES; | ||||
| 				CLANG_WARN_INFINITE_RECURSION = YES; | ||||
| 				CLANG_WARN_INT_CONVERSION = YES; | ||||
| 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||||
| 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||||
| 				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; | ||||
| 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||||
| 				CLANG_WARN_STRICT_PROTOTYPES = YES; | ||||
| 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||||
| 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | ||||
| 				CLANG_WARN_UNREACHABLE_CODE = YES; | ||||
| 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||||
| 				CODE_SIGN_IDENTITY = "iPhone Developer"; | ||||
| 				COPY_PHASE_STRIP = NO; | ||||
| 				DEBUG_INFORMATION_FORMAT = dwarf; | ||||
| 				ENABLE_STRICT_OBJC_MSGSEND = YES; | ||||
| 				ENABLE_TESTABILITY = YES; | ||||
| 				GCC_C_LANGUAGE_STANDARD = gnu11; | ||||
| 				GCC_DYNAMIC_NO_PIC = NO; | ||||
| 				GCC_NO_COMMON_BLOCKS = YES; | ||||
| 				GCC_OPTIMIZATION_LEVEL = 0; | ||||
| 				GCC_PREPROCESSOR_DEFINITIONS = ( | ||||
| 					"DEBUG=1", | ||||
| 					"$(inherited)", | ||||
| 				); | ||||
| 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||||
| 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||||
| 				GCC_WARN_UNDECLARED_SELECTOR = YES; | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; | ||||
| 				MTL_FAST_MATH = YES; | ||||
| 				ONLY_ACTIVE_ARCH = YES; | ||||
| 				SDKROOT = iphoneos; | ||||
| 			}; | ||||
| 			name = Debug; | ||||
| 		}; | ||||
| 		CF39D453222BC07E001EF57F /* Release */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | ||||
| 				CLANG_ENABLE_OBJC_WEAK = YES; | ||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||
| 				CLANG_WARN_BOOL_CONVERSION = YES; | ||||
| 				CLANG_WARN_COMMA = YES; | ||||
| 				CLANG_WARN_CONSTANT_CONVERSION = YES; | ||||
| 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||||
| 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||||
| 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES; | ||||
| 				CLANG_WARN_EMPTY_BODY = YES; | ||||
| 				CLANG_WARN_ENUM_CONVERSION = YES; | ||||
| 				CLANG_WARN_INFINITE_RECURSION = YES; | ||||
| 				CLANG_WARN_INT_CONVERSION = YES; | ||||
| 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||||
| 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||||
| 				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; | ||||
| 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||||
| 				CLANG_WARN_STRICT_PROTOTYPES = YES; | ||||
| 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||||
| 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; | ||||
| 				CLANG_WARN_UNREACHABLE_CODE = YES; | ||||
| 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||||
| 				CODE_SIGN_IDENTITY = "iPhone Developer"; | ||||
| 				COPY_PHASE_STRIP = NO; | ||||
| 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||||
| 				ENABLE_NS_ASSERTIONS = NO; | ||||
| 				ENABLE_STRICT_OBJC_MSGSEND = YES; | ||||
| 				GCC_C_LANGUAGE_STANDARD = gnu11; | ||||
| 				GCC_NO_COMMON_BLOCKS = YES; | ||||
| 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||||
| 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||||
| 				GCC_WARN_UNDECLARED_SELECTOR = YES; | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = NO; | ||||
| 				MTL_FAST_MATH = YES; | ||||
| 				SDKROOT = iphoneos; | ||||
| 				SWIFT_COMPILATION_MODE = wholemodule; | ||||
| 				VALIDATE_PRODUCT = YES; | ||||
| 			}; | ||||
| 			name = Release; | ||||
| 		}; | ||||
| 		CF39D455222BC07E001EF57F /* Debug */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			baseConfigurationReference = 9FD89BFF752E9D684C11E887 /* Pods-Alderis Demo.debug.xcconfig */; | ||||
| 			buildSettings = { | ||||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| 				ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; | ||||
| 				ASSETCATALOG_COMPILER_SKIP_APP_STORE_DEPLOYMENT = YES; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CODE_SIGN_ENTITLEMENTS = "Alderis Demo/AlderisDemo.entitlements"; | ||||
| 				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; | ||||
| 				CODE_SIGN_STYLE = Automatic; | ||||
| 				DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; | ||||
| 				DEVELOPMENT_TEAM = N2LN9ZT493; | ||||
| 				INFOPLIST_FILE = "Alderis Demo/Info.plist"; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 				); | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = ws.hbang.AlderisDemo; | ||||
| 				PRODUCT_NAME = "Alderis Demo"; | ||||
| 				SUPPORTS_MACCATALYST = YES; | ||||
| 				SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; | ||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TARGETED_DEVICE_FAMILY = "1,2,6"; | ||||
| 			}; | ||||
| 			name = Debug; | ||||
| 		}; | ||||
| 		CF39D456222BC07E001EF57F /* Release */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			baseConfigurationReference = 2B5DB4BF0F331C22D5DFB481 /* Pods-Alderis Demo.release.xcconfig */; | ||||
| 			buildSettings = { | ||||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| 				ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; | ||||
| 				ASSETCATALOG_COMPILER_SKIP_APP_STORE_DEPLOYMENT = YES; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CODE_SIGN_ENTITLEMENTS = "Alderis Demo/AlderisDemo.entitlements"; | ||||
| 				CODE_SIGN_STYLE = Automatic; | ||||
| 				DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; | ||||
| 				INFOPLIST_FILE = "Alderis Demo/Info.plist"; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 				); | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = ws.hbang.AlderisDemo; | ||||
| 				PRODUCT_NAME = "Alderis Demo"; | ||||
| 				SUPPORTS_MACCATALYST = YES; | ||||
| 				SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TARGETED_DEVICE_FAMILY = "1,2,6"; | ||||
| 			}; | ||||
| 			name = Release; | ||||
| 		}; | ||||
| /* End XCBuildConfiguration section */ | ||||
|  | ||||
| /* Begin XCConfigurationList section */ | ||||
| 		CF39D439222BC07C001EF57F /* Build configuration list for PBXProject "Alderis Demo" */ = { | ||||
| 			isa = XCConfigurationList; | ||||
| 			buildConfigurations = ( | ||||
| 				CF39D452222BC07E001EF57F /* Debug */, | ||||
| 				CF39D453222BC07E001EF57F /* Release */, | ||||
| 			); | ||||
| 			defaultConfigurationIsVisible = 0; | ||||
| 			defaultConfigurationName = Release; | ||||
| 		}; | ||||
| 		CF39D454222BC07E001EF57F /* Build configuration list for PBXNativeTarget "Alderis Demo" */ = { | ||||
| 			isa = XCConfigurationList; | ||||
| 			buildConfigurations = ( | ||||
| 				CF39D455222BC07E001EF57F /* Debug */, | ||||
| 				CF39D456222BC07E001EF57F /* Release */, | ||||
| 			); | ||||
| 			defaultConfigurationIsVisible = 0; | ||||
| 			defaultConfigurationName = Release; | ||||
| 		}; | ||||
| /* End XCConfigurationList section */ | ||||
| 	}; | ||||
| 	rootObject = CF39D436222BC07C001EF57F /* Project object */; | ||||
| } | ||||
							
								
								
									
										7
									
								
								Tweaks/Alderis/Demo/Alderis Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
|    <FileRef | ||||
|       location = "self:Alderis Demo.xcodeproj"> | ||||
|    </FileRef> | ||||
| </Workspace> | ||||
| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>IDEDidComputeMac32BitWarning</key> | ||||
| 	<true/> | ||||
| </dict> | ||||
| </plist> | ||||
| @@ -0,0 +1,78 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Scheme | ||||
|    LastUpgradeVersion = "1330" | ||||
|    version = "1.3"> | ||||
|    <BuildAction | ||||
|       parallelizeBuildables = "YES" | ||||
|       buildImplicitDependencies = "YES"> | ||||
|       <BuildActionEntries> | ||||
|          <BuildActionEntry | ||||
|             buildForTesting = "YES" | ||||
|             buildForRunning = "YES" | ||||
|             buildForProfiling = "YES" | ||||
|             buildForArchiving = "YES" | ||||
|             buildForAnalyzing = "YES"> | ||||
|             <BuildableReference | ||||
|                BuildableIdentifier = "primary" | ||||
|                BlueprintIdentifier = "CF39D43D222BC07C001EF57F" | ||||
|                BuildableName = "Alderis Demo.app" | ||||
|                BlueprintName = "Alderis Demo" | ||||
|                ReferencedContainer = "container:Alderis Demo.xcodeproj"> | ||||
|             </BuildableReference> | ||||
|          </BuildActionEntry> | ||||
|       </BuildActionEntries> | ||||
|    </BuildAction> | ||||
|    <TestAction | ||||
|       buildConfiguration = "Debug" | ||||
|       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||||
|       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||||
|       shouldUseLaunchSchemeArgsEnv = "YES"> | ||||
|       <Testables> | ||||
|       </Testables> | ||||
|    </TestAction> | ||||
|    <LaunchAction | ||||
|       buildConfiguration = "Debug" | ||||
|       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||||
|       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||||
|       launchStyle = "0" | ||||
|       useCustomWorkingDirectory = "NO" | ||||
|       ignoresPersistentStateOnLaunch = "NO" | ||||
|       debugDocumentVersioning = "YES" | ||||
|       debugServiceExtension = "internal" | ||||
|       allowLocationSimulation = "YES"> | ||||
|       <BuildableProductRunnable | ||||
|          runnableDebuggingMode = "0"> | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "CF39D43D222BC07C001EF57F" | ||||
|             BuildableName = "Alderis Demo.app" | ||||
|             BlueprintName = "Alderis Demo" | ||||
|             ReferencedContainer = "container:Alderis Demo.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|       </BuildableProductRunnable> | ||||
|    </LaunchAction> | ||||
|    <ProfileAction | ||||
|       buildConfiguration = "Release" | ||||
|       shouldUseLaunchSchemeArgsEnv = "YES" | ||||
|       savedToolIdentifier = "" | ||||
|       useCustomWorkingDirectory = "NO" | ||||
|       debugDocumentVersioning = "YES"> | ||||
|       <BuildableProductRunnable | ||||
|          runnableDebuggingMode = "0"> | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "CF39D43D222BC07C001EF57F" | ||||
|             BuildableName = "Alderis Demo.app" | ||||
|             BlueprintName = "Alderis Demo" | ||||
|             ReferencedContainer = "container:Alderis Demo.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|       </BuildableProductRunnable> | ||||
|    </ProfileAction> | ||||
|    <AnalyzeAction | ||||
|       buildConfiguration = "Debug"> | ||||
|    </AnalyzeAction> | ||||
|    <ArchiveAction | ||||
|       buildConfiguration = "Release" | ||||
|       revealArchiveInOrganizer = "YES"> | ||||
|    </ArchiveAction> | ||||
| </Scheme> | ||||
							
								
								
									
										13
									
								
								Tweaks/Alderis/Demo/Alderis Demo.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
|    <FileRef | ||||
|       location = "group:../Alderis.xcodeproj"> | ||||
|    </FileRef> | ||||
|    <FileRef | ||||
|       location = "group:Alderis Demo.xcodeproj"> | ||||
|    </FileRef> | ||||
|    <FileRef | ||||
|       location = "group:Pods/Pods.xcodeproj"> | ||||
|    </FileRef> | ||||
| </Workspace> | ||||
| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>IDEDidComputeMac32BitWarning</key> | ||||
| 	<true/> | ||||
| </dict> | ||||
| </plist> | ||||
							
								
								
									
										10
									
								
								Tweaks/Alderis/Demo/Alderis Demo/AlderisDemo.entitlements
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,10 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>com.apple.security.app-sandbox</key> | ||||
| 	<true/> | ||||
| 	<key>com.apple.security.network.client</key> | ||||
| 	<true/> | ||||
| </dict> | ||||
| </plist> | ||||
							
								
								
									
										35
									
								
								Tweaks/Alderis/Demo/Alderis Demo/AppDelegate.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,35 @@ | ||||
| // | ||||
| //  AppDelegate.swift | ||||
| //  Alderis Demo | ||||
| // | ||||
| //  Created by Adam Demasi on 12/3/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
|  | ||||
| @UIApplicationMain | ||||
| class AppDelegate: UIResponder, UIApplicationDelegate { | ||||
|  | ||||
| 	var window: UIWindow? | ||||
|  | ||||
| 	// swiftlint:disable:next line_length | ||||
| 	func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { | ||||
| 		window = UIWindow(frame: UIScreen.main.bounds) | ||||
| 		window!.tintColor = UIColor(hue: 0.939614, saturation: 0.811765, brightness: 0.333333, alpha: 1) | ||||
|  | ||||
| 		let tabBarController = UITabBarController() | ||||
| 		let viewController = UINavigationController(rootViewController: FirstViewController()) | ||||
| 		if #available(iOS 13, *) { | ||||
| 			let tabIcon = UIImage(systemName: "paintbrush.fill")?.withBaselineOffset(fromBottom: 2) | ||||
| 			viewController.tabBarItem = UITabBarItem(title: "Alderis Demo", image: tabIcon, tag: 0) | ||||
| 		} | ||||
| 		tabBarController.viewControllers = [viewController] | ||||
|  | ||||
| 		window!.rootViewController = tabBarController | ||||
| 		window!.makeKeyAndVisible() | ||||
|  | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| } | ||||
| After Width: | Height: | Size: 4.7 KiB | 
| After Width: | Height: | Size: 5.9 KiB | 
| After Width: | Height: | Size: 6.6 KiB | 
| After Width: | Height: | Size: 7.1 KiB | 
| @@ -0,0 +1,102 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "2x", | ||||
|       "size" : "20x20" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "3x", | ||||
|       "size" : "20x20" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "2x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "3x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "2x", | ||||
|       "size" : "40x40" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "3x", | ||||
|       "size" : "40x40" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "AppIcon120x120.png", | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "2x", | ||||
|       "size" : "60x60" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "AppIcon180x180.png", | ||||
|       "idiom" : "iphone", | ||||
|       "scale" : "3x", | ||||
|       "size" : "60x60" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "1x", | ||||
|       "size" : "20x20" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "20x20" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "1x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "29x29" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "1x", | ||||
|       "size" : "40x40" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "40x40" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "1x", | ||||
|       "size" : "76x76" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "AppIcon152x152.png", | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "76x76" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "AppIcon167x167.png", | ||||
|       "idiom" : "ipad", | ||||
|       "scale" : "2x", | ||||
|       "size" : "83.5x83.5" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "ios-marketing", | ||||
|       "scale" : "1x", | ||||
|       "size" : "1024x1024" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,6 @@ | ||||
| { | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | ||||
|     <dependencies> | ||||
|         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/> | ||||
|         <capability name="Safe area layout guides" minToolsVersion="9.0"/> | ||||
|         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||||
|     </dependencies> | ||||
|     <scenes> | ||||
|         <!--View Controller--> | ||||
|         <scene sceneID="EHf-IW-A2E"> | ||||
|             <objects> | ||||
|                 <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | ||||
|                     <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | ||||
|                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> | ||||
|                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||||
|                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | ||||
|                         <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> | ||||
|                     </view> | ||||
|                 </viewController> | ||||
|                 <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | ||||
|             </objects> | ||||
|             <point key="canvasLocation" x="53" y="375"/> | ||||
|         </scene> | ||||
|     </scenes> | ||||
| </document> | ||||
							
								
								
									
										321
									
								
								Tweaks/Alderis/Demo/Alderis Demo/FirstViewController.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,321 @@ | ||||
| // | ||||
| //  FirstViewController.swift | ||||
| //  Alderis Demo | ||||
| // | ||||
| //  Created by Adam Demasi on 15/4/20. | ||||
| //  Copyright © 2020 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| import UIKit | ||||
| import Alderis | ||||
|  | ||||
| class FirstViewController: UIViewController { | ||||
|  | ||||
| 	private var color = UIColor(hue: 0.939614, saturation: 0.811765, brightness: 0.333333, alpha: 1) | ||||
|  | ||||
| 	private var colorWell: ColorWell! | ||||
| 	private var uikitWell: UIView? | ||||
|  | ||||
| 	// swiftlint:disable:next function_body_length | ||||
| 	override func viewDidLoad() { | ||||
| 		super.viewDidLoad() | ||||
|  | ||||
| 		title = "Alderis Demo" | ||||
| 		if #available(iOS 13, *) { | ||||
| 			view.backgroundColor = .systemBackground | ||||
| 		} else { | ||||
| 			view.backgroundColor = .white | ||||
| 		} | ||||
|  | ||||
| 		#if targetEnvironment(macCatalyst) | ||||
| 		navigationController?.isNavigationBarHidden = true | ||||
| 		#endif | ||||
|  | ||||
| 		let stackView = UIStackView() | ||||
| 		stackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		stackView.axis = .vertical | ||||
| 		stackView.alignment = .center | ||||
| 		stackView.spacing = 10 | ||||
| 		view.addSubview(stackView) | ||||
|  | ||||
| 		let mainButton = UIButton(type: .system) | ||||
| 		if #available(iOS 15, *) { | ||||
| 			var config = UIButton.Configuration.filled() | ||||
| 			config.buttonSize = .large | ||||
| 			mainButton.configuration = config | ||||
| 		} else { | ||||
| 			mainButton.titleLabel!.font = UIFont.systemFont(ofSize: 34, weight: .semibold) | ||||
| 		} | ||||
| 		mainButton.setTitle("Present", for: .normal) | ||||
| 		mainButton.addTarget(self, action: #selector(self.presentColorPicker), for: .touchUpInside) | ||||
| 		stackView.addArrangedSubview(mainButton) | ||||
|  | ||||
| 		// swiftlint:disable comma | ||||
| 		let buttons: [(title: String, action: Selector)] = [ | ||||
| 			("Present with customised title",       #selector(presentColorPickerCustomisedTitle)), | ||||
| 			("Present with customised initial tab", #selector(presentColorPickerCustomisedInitialTab)), | ||||
| 			("Present with customised tabs",        #selector(presentColorPickerCustomisedTabs)), | ||||
| 			("Present with tabs hidden",            #selector(presentColorPickerNoTabs)), | ||||
| 			("Present with customised title, tabs hidden", #selector(presentColorPickerCustomisedTitleNoTabs)), | ||||
| 			("Present without alpha",               #selector(presentColorPickerNoAlpha)), | ||||
| 			("Present without overriding Smart Invert", #selector(presentColorPickerNoOverrideSmartInvert)), | ||||
| 			("Present using deprecated API",        #selector(presentColorPickerDeprecatedAPI)), | ||||
| 			("Present UIKit Color Picker",          #selector(presentUIKitColorPicker)) | ||||
| 		] | ||||
| 		// swiftlint:enable comma | ||||
|  | ||||
| 		for item in buttons { | ||||
| 			let button = UIButton(type: .system) | ||||
| 			if #available(iOS 15, *) { | ||||
| 				var config = UIButton.Configuration.plain() | ||||
| 				config.buttonSize = .mini | ||||
| 				config.macIdiomStyle = .borderlessTinted | ||||
| 				button.configuration = config | ||||
| 			} | ||||
| 			button.setTitle(item.title, for: .normal) | ||||
| 			button.addTarget(self, action: item.action, for: .touchUpInside) | ||||
| 			stackView.addArrangedSubview(button) | ||||
| 		} | ||||
|  | ||||
| 		let spacerView = UIView() | ||||
| 		stackView.addArrangedSubview(spacerView) | ||||
|  | ||||
| 		let wellsLabel = UILabel() | ||||
| 		wellsLabel.font = UIFont.preferredFont(forTextStyle: .headline) | ||||
| 		wellsLabel.textAlignment = .center | ||||
| 		wellsLabel.text = "Color wells (try out drag and drop!)" | ||||
| 		stackView.addArrangedSubview(wellsLabel) | ||||
|  | ||||
| 		colorWell = ColorWell() | ||||
| 		colorWell.isDragInteractionEnabled = true | ||||
| 		colorWell.isDropInteractionEnabled = true | ||||
| 		colorWell.addTarget(self, action: #selector(self.colorWellValueChanged(_:)), for: .valueChanged) | ||||
| 		colorWell.addTarget(self, action: #selector(self.presentColorPicker), for: .touchUpInside) | ||||
|  | ||||
| 		let dragOrDropColorWell = ColorWell() | ||||
| 		dragOrDropColorWell.isDragInteractionEnabled = true | ||||
| 		dragOrDropColorWell.isDropInteractionEnabled = true | ||||
| 		dragOrDropColorWell.color = .systemPurple | ||||
|  | ||||
| 		let nonDraggableWell = ColorWell() | ||||
| 		nonDraggableWell.isDragInteractionEnabled = false | ||||
| 		nonDraggableWell.isDropInteractionEnabled = true | ||||
| 		nonDraggableWell.color = .systemOrange | ||||
|  | ||||
| 		let nonDroppableWell = ColorWell() | ||||
| 		nonDroppableWell.isDragInteractionEnabled = true | ||||
| 		nonDroppableWell.isDropInteractionEnabled = false | ||||
| 		nonDroppableWell.color = .systemTeal | ||||
|  | ||||
| 		let nonDragOrDropWell = ColorWell() | ||||
| 		nonDragOrDropWell.isDragInteractionEnabled = false | ||||
| 		nonDragOrDropWell.isDropInteractionEnabled = false | ||||
| 		nonDragOrDropWell.color = .systemGreen | ||||
|  | ||||
| 		let wellsStackView = UIStackView(arrangedSubviews: [colorWell, | ||||
| 																												dragOrDropColorWell, | ||||
| 																												nonDraggableWell, | ||||
| 																												nonDroppableWell, | ||||
| 																												nonDragOrDropWell]) | ||||
| 		wellsStackView.translatesAutoresizingMaskIntoConstraints = false | ||||
| 		wellsStackView.axis = .horizontal | ||||
| 		wellsStackView.alignment = .center | ||||
| 		wellsStackView.spacing = 10 | ||||
|  | ||||
| 		if #available(iOS 14, *) { | ||||
| 			let uikitWell = UIColorWell() | ||||
| 			uikitWell.addTarget(self, action: #selector(self.uikitColorWellValueChanged(_:)), for: .valueChanged) | ||||
| 			wellsStackView.addArrangedSubview(uikitWell) | ||||
| 			self.uikitWell = uikitWell | ||||
| 		} | ||||
|  | ||||
| 		stackView.addArrangedSubview(wellsStackView) | ||||
|  | ||||
| 		NSLayoutConstraint.activate([ | ||||
| 			stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15), | ||||
| 			stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15), | ||||
| 			stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor), | ||||
|  | ||||
| 			spacerView.heightAnchor.constraint(equalToConstant: 0) | ||||
| 		]) | ||||
|  | ||||
| 		var isMac = false | ||||
| 		if #available(iOS 14, *) { | ||||
| 			isMac = UIDevice.current.userInterfaceIdiom == .mac | ||||
| 		} | ||||
|  | ||||
| 		NSLayoutConstraint.activate(wellsStackView.arrangedSubviews.flatMap { view in | ||||
| 			[ | ||||
| 				view.widthAnchor.constraint(equalToConstant: isMac ? 24 : 32), | ||||
| 				view.heightAnchor.constraint(equalTo: view.widthAnchor) | ||||
| 			] | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	override func viewDidAppear(_ animated: Bool) { | ||||
| 		super.viewDidAppear(animated) | ||||
|  | ||||
| 		view.window!.tintColor = color | ||||
| 		colorWell.color = color | ||||
| 		if #available(iOS 14, *), | ||||
| 			 let uikitWell = uikitWell as? UIColorWell { | ||||
| 			uikitWell.selectedColor = color | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@objc func colorWellValueChanged(_ sender: ColorWell) { | ||||
| 		NSLog("Color well value changed with value %@", String(describing: sender.color)) | ||||
| 		view.window!.tintColor = sender.color | ||||
| 		if #available(iOS 14, *), | ||||
| 			 let uikitWell = uikitWell as? UIColorWell { | ||||
| 			uikitWell.selectedColor = sender.color | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@available(iOS 14, *) | ||||
| 	@objc func uikitColorWellValueChanged(_ sender: UIColorWell) { | ||||
| 		NSLog("UIKit color well value changed with value %@", String(describing: sender.selectedColor)) | ||||
| 		view.window!.tintColor = sender.selectedColor | ||||
| 		colorWell.color = sender.selectedColor | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPicker(_ sender: UIView) { | ||||
| 		let configuration = ColorPickerConfiguration(color: color) | ||||
| 		let colorPickerViewController = ColorPickerViewController(configuration: configuration) | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPickerCustomisedTitle(_ sender: UIView) { | ||||
| 		let configuration = ColorPickerConfiguration(color: color) | ||||
| 		configuration.title = "Select an Awesome Color" | ||||
|  | ||||
| 		let colorPickerViewController = ColorPickerViewController(configuration: configuration) | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPickerCustomisedInitialTab(_ sender: UIView) { | ||||
| 		let configuration = ColorPickerConfiguration(color: color) | ||||
| 		configuration.initialTab = .map | ||||
|  | ||||
| 		let colorPickerViewController = ColorPickerViewController(configuration: configuration) | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPickerCustomisedTabs(_ sender: UIView) { | ||||
| 		let configuration = ColorPickerConfiguration(color: color) | ||||
| 		configuration.visibleTabs = [.map, .sliders] | ||||
| 		configuration.initialTab = .sliders | ||||
|  | ||||
| 		let colorPickerViewController = ColorPickerViewController(configuration: configuration) | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPickerNoAlpha(_ sender: UIView) { | ||||
| 		let configuration = ColorPickerConfiguration(color: color.withAlphaComponent(0.5)) | ||||
| 		configuration.supportsAlpha = false | ||||
|  | ||||
| 		let colorPickerViewController = ColorPickerViewController(configuration: configuration) | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPickerNoTabs(_ sender: UIView) { | ||||
| 		let configuration = ColorPickerConfiguration(color: color) | ||||
| 		configuration.showTabs = false | ||||
|  | ||||
| 		let colorPickerViewController = ColorPickerViewController(configuration: configuration) | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPickerCustomisedTitleNoTabs(_ sender: UIView) { | ||||
| 		let configuration = ColorPickerConfiguration(color: color) | ||||
| 		configuration.title = "Select an Awesome Color" | ||||
| 		configuration.showTabs = false | ||||
|  | ||||
| 		let colorPickerViewController = ColorPickerViewController(configuration: configuration) | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPickerNoOverrideSmartInvert(_ sender: UIView) { | ||||
| 		let configuration = ColorPickerConfiguration(color: color) | ||||
| 		configuration.overrideSmartInvert = false | ||||
|  | ||||
| 		let colorPickerViewController = ColorPickerViewController(configuration: configuration) | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentColorPickerDeprecatedAPI(_ sender: UIView) { | ||||
| 		let colorPickerViewController = ColorPickerViewController() | ||||
| 		colorPickerViewController.delegate = self | ||||
| 		colorPickerViewController.color = color | ||||
| 		colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 		tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 	} | ||||
|  | ||||
| 	@objc func presentUIKitColorPicker(_ sender: UIView) { | ||||
| 		if #available(iOS 14, *) { | ||||
| 			let colorPickerViewController = UIColorPickerViewController() | ||||
| 			colorPickerViewController.delegate = self | ||||
| 			colorPickerViewController.selectedColor = color | ||||
| 			colorPickerViewController.popoverPresentationController?.sourceView = sender | ||||
| 			tabBarController!.present(colorPickerViewController, animated: true) | ||||
| 		} else { | ||||
| 			fatalError("UIColorPickerViewController is only available as of iOS 14") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| extension FirstViewController: ColorPickerDelegate { | ||||
|  | ||||
| 	func colorPicker(_ colorPicker: ColorPickerViewController, didSelect color: UIColor) { | ||||
| 		NSLog("User selected color %@ (%@)", color.propertyListValue, String(describing: color)) | ||||
| 		self.color = color | ||||
| 		view.window!.tintColor = color | ||||
| 		colorWell.color = color | ||||
| 	} | ||||
|  | ||||
| 	func colorPicker(_ colorPicker: ColorPickerViewController, didAccept color: UIColor) { | ||||
| 		NSLog("User accepted color %@ (%@)", color.propertyListValue, String(describing: color)) | ||||
| 	} | ||||
|  | ||||
| 	func colorPickerDidCancel(_ colorPicker: ColorPickerViewController) { | ||||
| 		NSLog("Color picker cancelled") | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| @available(iOS 14, *) | ||||
| extension FirstViewController: UIColorPickerViewControllerDelegate { | ||||
|  | ||||
| 	func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) { | ||||
| 		NSLog("UIKit color picker value changed with color %@ (%@)", | ||||
| 					viewController.selectedColor.propertyListValue, | ||||
| 					String(describing: viewController.selectedColor)) | ||||
| 		color = viewController.selectedColor | ||||
| 		view.window!.tintColor = viewController.selectedColor | ||||
| 		if let uikitWell = uikitWell as? UIColorWell { | ||||
| 			uikitWell.selectedColor = viewController.selectedColor | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) { | ||||
| 		NSLog("UIKit color picker finished") | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| // | ||||
| //  HBColorPickerSectionHeaderView.h | ||||
| //  Alderis Demo | ||||
| // | ||||
| //  Created by Adam Demasi on 31/3/19. | ||||
| //  Copyright © 2019 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| NS_ASSUME_NONNULL_BEGIN | ||||
|  | ||||
| @interface HBColorPickerSectionHeaderView : UICollectionReusableView | ||||
|  | ||||
| @property (nonatomic, strong) UILabel *titleLabel; | ||||
|  | ||||
| @end | ||||
|  | ||||
| NS_ASSUME_NONNULL_END | ||||
| @@ -0,0 +1,43 @@ | ||||
| // | ||||
| //  HBColorPickerSectionHeaderView.m | ||||
| //  Alderis Demo | ||||
| // | ||||
| //  Created by Adam Demasi on 31/3/19. | ||||
| //  Copyright © 2019 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "HBColorPickerSectionHeaderView.h" | ||||
| #import "CompactConstraint.h" | ||||
|  | ||||
| @implementation HBColorPickerSectionHeaderView | ||||
|  | ||||
| - (instancetype)initWithFrame:(CGRect)frame { | ||||
| 	self = [super initWithFrame:frame]; | ||||
|  | ||||
| 	if (self) { | ||||
| 		_titleLabel = [[UILabel alloc] init]; | ||||
| 		_titleLabel.translatesAutoresizingMaskIntoConstraints = NO; | ||||
| 		_titleLabel.font = [UIFont boldSystemFontOfSize:18.f]; | ||||
| 		[self addSubview:_titleLabel]; | ||||
|  | ||||
| 		[self hb_addCompactConstraints:@[ | ||||
| 																		 @"titleLabel.left = self.left + horizontalMargin", | ||||
| 																		 @"titleLabel.right = self.right - horizontalMargin", | ||||
| 																		 @"titleLabel.top = self.top + topMargin", | ||||
| 																		 @"titleLabel.bottom = self.bottom - bottomMargin" | ||||
| 																		 ] | ||||
| 													 metrics:@{ | ||||
| 																		 @"horizontalMargin": @15.f, | ||||
| 																		 @"topMargin": @10.f, | ||||
| 																		 @"bottomMargin": @0.f | ||||
| 																		 } | ||||
| 														 views:@{ | ||||
| 																		 @"self": self, | ||||
| 																		 @"titleLabel": _titleLabel | ||||
| 																		 }]; | ||||
| 	} | ||||
| 	 | ||||
| 	return self; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										17
									
								
								Tweaks/Alderis/Demo/Alderis Demo/HBColorPickerSwatchCell.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,17 @@ | ||||
| // | ||||
| //  HBColorPickerSwatchCell.h | ||||
| //  Alderis Demo | ||||
| // | ||||
| //  Created by Adam Demasi on 4/3/19. | ||||
| //  Copyright © 2019 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import <UIKit/UIKit.h> | ||||
|  | ||||
| NS_ASSUME_NONNULL_BEGIN | ||||
|  | ||||
| @interface HBColorPickerSwatchCell : UICollectionViewCell | ||||
|  | ||||
| @end | ||||
|  | ||||
| NS_ASSUME_NONNULL_END | ||||
							
								
								
									
										32
									
								
								Tweaks/Alderis/Demo/Alderis Demo/HBColorPickerSwatchCell.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,32 @@ | ||||
| // | ||||
| //  HBColorPickerSwatchCell.m | ||||
| //  Alderis Demo | ||||
| // | ||||
| //  Created by Adam Demasi on 4/3/19. | ||||
| //  Copyright © 2019 HASHBANG Productions. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "HBColorPickerSwatchCell.h" | ||||
|  | ||||
| @implementation HBColorPickerSwatchCell | ||||
|  | ||||
| - (instancetype)initWithFrame:(CGRect)frame { | ||||
| 	self = [super initWithFrame:frame]; | ||||
| 	 | ||||
| 	if (self) { | ||||
| 		self.clipsToBounds = YES; | ||||
| //		self.layer.cornerRadius = 22.f; | ||||
| //		self.layer.borderColor = [UIColor grayColor].CGColor; | ||||
| 	} | ||||
| 	 | ||||
| 	return self; | ||||
| } | ||||
|  | ||||
| - (void)didMoveToWindow { | ||||
| 	[super didMoveToWindow]; | ||||
|  | ||||
| //	CGFloat scale = self.window.screen.scale ?: 1.f; | ||||
| //	self.layer.borderWidth = scale > 2.f ? 2.f / scale : 1.f / scale; | ||||
| } | ||||
|  | ||||
| @end | ||||
							
								
								
									
										45
									
								
								Tweaks/Alderis/Demo/Alderis Demo/Info.plist
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,45 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>CFBundleDevelopmentRegion</key> | ||||
| 	<string>$(DEVELOPMENT_LANGUAGE)</string> | ||||
| 	<key>CFBundleExecutable</key> | ||||
| 	<string>$(EXECUTABLE_NAME)</string> | ||||
| 	<key>CFBundleIdentifier</key> | ||||
| 	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||||
| 	<key>CFBundleInfoDictionaryVersion</key> | ||||
| 	<string>6.0</string> | ||||
| 	<key>CFBundleName</key> | ||||
| 	<string>Alderis Demo</string> | ||||
| 	<key>CFBundleDisplayName</key> | ||||
| 	<string>Alderis Demo</string> | ||||
| 	<key>CFBundlePackageType</key> | ||||
| 	<string>APPL</string> | ||||
| 	<key>CFBundleShortVersionString</key> | ||||
| 	<string>1.0</string> | ||||
| 	<key>CFBundleVersion</key> | ||||
| 	<string>1</string> | ||||
| 	<key>LSRequiresIPhoneOS</key> | ||||
| 	<true/> | ||||
| 	<key>UILaunchStoryboardName</key> | ||||
| 	<string>Launch Screen</string> | ||||
| 	<key>UIRequiredDeviceCapabilities</key> | ||||
| 	<array> | ||||
| 		<string>armv7</string> | ||||
| 	</array> | ||||
| 	<key>UISupportedInterfaceOrientations</key> | ||||
| 	<array> | ||||
| 		<string>UIInterfaceOrientationPortrait</string> | ||||
| 		<string>UIInterfaceOrientationLandscapeLeft</string> | ||||
| 		<string>UIInterfaceOrientationLandscapeRight</string> | ||||
| 	</array> | ||||
| 	<key>UISupportedInterfaceOrientations~ipad</key> | ||||
| 	<array> | ||||
| 		<string>UIInterfaceOrientationPortrait</string> | ||||
| 		<string>UIInterfaceOrientationPortraitUpsideDown</string> | ||||
| 		<string>UIInterfaceOrientationLandscapeLeft</string> | ||||
| 		<string>UIInterfaceOrientationLandscapeRight</string> | ||||
| 	</array> | ||||
| </dict> | ||||
| </plist> | ||||
							
								
								
									
										27
									
								
								Tweaks/Alderis/Demo/Alderis Demo/Launch Screen.storyboard
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,27 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | ||||
|     <device id="retina6_1" orientation="portrait" appearance="light"/> | ||||
|     <dependencies> | ||||
|         <deployment identifier="iOS"/> | ||||
|         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> | ||||
|         <capability name="Safe area layout guides" minToolsVersion="9.0"/> | ||||
|         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||||
|     </dependencies> | ||||
|     <scenes> | ||||
|         <!--View Controller--> | ||||
|         <scene sceneID="EHf-IW-A2E"> | ||||
|             <objects> | ||||
|                 <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | ||||
|                     <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | ||||
|                         <rect key="frame" x="0.0" y="0.0" width="414" height="896"/> | ||||
|                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||||
|                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | ||||
|                         <viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/> | ||||
|                     </view> | ||||
|                 </viewController> | ||||
|                 <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | ||||
|             </objects> | ||||
|             <point key="canvasLocation" x="53" y="375"/> | ||||
|         </scene> | ||||
|     </scenes> | ||||
| </document> | ||||
 Balackburn
					Balackburn