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>
|