├── Art └── toggle_design.pcvd ├── .gitignore ├── Demo └── DSFToggleButton Demo │ ├── DSFToggleButton Demo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── DSFToggleButton_Demo.entitlements │ ├── Info.plist │ └── AppDelegate.swift │ ├── DSFToggleButton Demo Objc │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.h │ ├── DSFToggleButton_Demo_Objc.entitlements │ ├── main.m │ ├── Info.plist │ ├── AppDelegate.m │ └── Base.lproj │ │ └── MainMenu.xib │ ├── DSFToggleButton_Debugger │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── DSFToggleButton_Debugger.entitlements │ ├── Info.plist │ └── AppDelegate.swift │ ├── DSFToggleButton SwiftUI Demo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── DSFToggleButton_SwiftUI_Demo.entitlements │ ├── DSFToggleButton_SwiftUI_DemoApp.swift │ ├── Info.plist │ └── ContentView.swift │ └── DSFToggleButton Demo.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved │ ├── xcshareddata │ └── xcschemes │ │ ├── All.xcscheme │ │ ├── DSFToggleButton Demo.xcscheme │ │ └── DSFToggleButton Demo Objc.xcscheme │ └── project.pbxproj ├── .spi.yml ├── Tests └── DSFToggleButtonTests │ └── DSFToggleButtonTests.swift ├── .swiftpm └── configuration │ └── Package.resolved ├── Package.swift ├── LICENSE ├── README.md └── Sources └── DSFToggleButton ├── DSFToggleButton+extensions.swift ├── DSFToggleButton+SwiftUI.swift └── DSFToggleButton.swift /Art/toggle_design.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagronf/DSFToggleButton/HEAD/Art/toggle_design.pcvd -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | contents.xcworkspacedata 7 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo Objc/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton_Debugger/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton SwiftUI Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | builder: 3 | configs: 4 | - platform: macos-xcodebuild 5 | scheme: DSFToggleButton 6 | - platform: macos-xcodebuild-arm 7 | scheme: DSFToggleButton 8 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton SwiftUI Demo/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton SwiftUI Demo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo Objc/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // DSFToggleButton Demo Objc 4 | // 5 | // Created by Darren Ford on 18/2/20. 6 | // Copyright © 2020 Darren Ford. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/DSFToggleButtonTests/DSFToggleButtonTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import DSFToggleButton 3 | 4 | final class DSFToggleButtonTests: XCTestCase { 5 | func testExample() { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | //XCTAssertEqual(DSFToggleButton().text, "Hello, World!") 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo/DSFToggleButton_Demo.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.swiftpm/configuration/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "DSFAppearanceManager", 6 | "repositoryURL": "https://github.com/dagronf/DSFAppearanceManager", 7 | "state": { 8 | "branch": null, 9 | "revision": "3fe782f4b7f0aaceb3949133f15df898aa32a34f", 10 | "version": "3.5.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo Objc/DSFToggleButton_Demo_Objc.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton_Debugger/DSFToggleButton_Debugger.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton SwiftUI Demo/DSFToggleButton_SwiftUI_Demo.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo Objc/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DSFToggleButton Demo Objc 4 | // 5 | // Created by Darren Ford on 18/2/20. 6 | // Copyright © 2020 Darren Ford. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | @autoreleasepool { 13 | // Setup code that might create autoreleased objects goes here. 14 | } 15 | return NSApplicationMain(argc, argv); 16 | } 17 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton SwiftUI Demo/DSFToggleButton_SwiftUI_DemoApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFToggleButton_SwiftUI_DemoApp.swift 3 | // DSFToggleButton SwiftUI Demo 4 | // 5 | // Created by Darren Ford on 11/1/21. 6 | // Copyright © 2021 Darren Ford. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | @main 12 | struct DSFToggleButton_SwiftUI_DemoApp: App { 13 | var body: some Scene { 14 | WindowGroup { 15 | ContentView() 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "DSFAppearanceManager", 6 | "repositoryURL": "https://github.com/dagronf/DSFAppearanceManager", 7 | "state": { 8 | "branch": null, 9 | "revision": "3fe782f4b7f0aaceb3949133f15df898aa32a34f", 10 | "version": "3.5.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.4 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "DSFToggleButton", 7 | platforms: [ 8 | .macOS(.v10_13) 9 | ], 10 | products: [ 11 | .library(name: "DSFToggleButton", targets: ["DSFToggleButton"]), 12 | .library(name: "DSFToggleButton-static", type: .static, targets: ["DSFToggleButton"]), 13 | .library(name: "DSFToggleButton-shared", type: .dynamic, targets: ["DSFToggleButton"]), 14 | ], 15 | dependencies: [ 16 | .package(url: "https://github.com/dagronf/DSFAppearanceManager", .upToNextMinor(from: "3.5.0")) 17 | ], 18 | targets: [ 19 | .target( 20 | name: "DSFToggleButton", 21 | dependencies: ["DSFAppearanceManager"]), 22 | .testTarget( 23 | name: "DSFToggleButtonTests", 24 | dependencies: ["DSFToggleButton"]), 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton SwiftUI Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSMinimumSystemVersion 22 | $(MACOSX_DEPLOYMENT_TARGET) 23 | NSHumanReadableCopyright 24 | Copyright © 2021 Darren Ford. All rights reserved. 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Darren Ford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton_Debugger/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo Objc/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton SwiftUI Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2020 Darren Ford. All rights reserved. 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | NSSupportsAutomaticTermination 32 | 33 | NSSupportsSuddenTermination 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo Objc/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2020 Darren Ford. All rights reserved. 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | NSSupportsAutomaticTermination 32 | 33 | NSSupportsSuddenTermination 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton_Debugger/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2020 Darren Ford. All rights reserved. 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | NSSupportsAutomaticTermination 32 | 33 | NSSupportsSuddenTermination 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton SwiftUI Demo/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // DSFToggleButton SwiftUI Demo 4 | // 5 | // Created by Darren Ford on 11/1/21. 6 | // Copyright © 2021 Darren Ford. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | import DSFToggleButton 12 | 13 | struct ContentView: View { 14 | 15 | @State var state: NSControl.StateValue = .on 16 | 17 | @State var isEnabled: NSControl.StateValue = .on 18 | 19 | var body: some View { 20 | VStack { 21 | DSFToggleButton.SwiftUI( 22 | state: $state, 23 | isEnabled: self.isEnabled == .on, 24 | controlColor: NSColor.systemIndigo.cgColor, 25 | action: { state in 26 | // Called in response to the user changing the value 27 | Swift.print("ACTION: \(state)") 28 | }, 29 | stateChanged: { state in 30 | // This gets called for EVERY change, regardless of whether its user or program initiated 31 | Swift.print("STATECHANGE: \(state)") 32 | }) 33 | .frame(height: 40, alignment: .center) 34 | .padding() 35 | 36 | DSFToggleButton.SwiftUI( 37 | state: $isEnabled, 38 | controlColor: NSColor.systemGreen.cgColor 39 | ) 40 | .frame(height: 80, alignment: .center) 41 | .padding() 42 | 43 | DSFToggleButton.SwiftUI( 44 | state: $isEnabled, 45 | controlColor: NSColor.systemYellow.cgColor, 46 | showLabels: false 47 | ) 48 | .frame(height: 80, alignment: .center) 49 | .padding() 50 | 51 | Text("Hello, world!") 52 | .padding() 53 | 54 | Button("Toggle", action: { 55 | self.state = (self.state == .on) ? .off : .on 56 | }) 57 | 58 | Button("info") { 59 | Swift.print("self.state = \(self.state)") 60 | } 61 | 62 | Spacer() 63 | } 64 | } 65 | } 66 | 67 | struct ContentView_Previews: PreviewProvider { 68 | static var previews: some View { 69 | ContentView() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DSFToggleButton Demo 4 | // 5 | // Created by Darren Ford on 17/2/20. 6 | // Copyright © 2020 Darren Ford. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | import DSFToggleButton 12 | 13 | @NSApplicationMain 14 | class AppDelegate: NSObject, NSApplicationDelegate { 15 | 16 | @IBOutlet weak var window: NSWindow! 17 | 18 | 19 | @IBOutlet weak var allButton: DSFToggleButton! 20 | @IBOutlet weak var redButton: DSFToggleButton! 21 | @IBOutlet weak var greenButton: DSFToggleButton! 22 | @IBOutlet weak var blueButton: DSFToggleButton! 23 | @IBOutlet weak var yellowButton: DSFToggleButton! 24 | 25 | @IBOutlet weak var enableButton: DSFToggleButton! 26 | 27 | @IBAction func toggle(_ sender: DSFToggleButton) { 28 | self.redButton.toggle() 29 | self.greenButton.toggle() 30 | self.blueButton.toggle() 31 | self.yellowButton.toggle() 32 | } 33 | 34 | @IBAction func allButtonChanged(_ sender: DSFToggleButton) { 35 | self.redButton.state = sender.state 36 | self.greenButton.state = sender.state 37 | self.blueButton.state = sender.state 38 | self.yellowButton.state = sender.state 39 | } 40 | 41 | 42 | func applicationDidFinishLaunching(_ aNotification: Notification) { 43 | // Insert code here to initialize your application 44 | self.yellowButton.stateChangeBlock = { (button) in 45 | Swift.print("Yellow changed: \(button.state)") 46 | } 47 | 48 | } 49 | 50 | func applicationWillTerminate(_ aNotification: Notification) { 51 | // Insert code here to tear down your application 52 | } 53 | 54 | @IBAction func toggleEnabled(_ sender: Any) { 55 | self.enableButton.isEnabled.toggle() 56 | } 57 | 58 | } 59 | 60 | extension NSControl.StateValue: CustomStringConvertible { 61 | public var description: String { 62 | switch self { 63 | case .on: return NSLocalizedString("On", comment: "State for when a control is 'on'") 64 | case .mixed: return NSLocalizedString("Mixed", comment: "State for when a control is neither on of off") 65 | case .off: return NSLocalizedString("Off", comment: "State for when a control is 'off'") 66 | default: fatalError("unimplemented state") 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo Objc/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // DSFToggleButton Demo Objc 4 | // 5 | // Created by Darren Ford on 18/2/20. 6 | // Copyright © 2020 Darren Ford. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @import DSFToggleButton; 12 | 13 | @interface AppDelegate () 14 | 15 | @property (weak) IBOutlet NSWindow *window; 16 | 17 | @property (weak) IBOutlet DSFToggleButton *red; 18 | @property (weak) IBOutlet DSFToggleButton *green; 19 | @property (weak) IBOutlet DSFToggleButton *blue; 20 | @property (weak) IBOutlet DSFToggleButton *all; 21 | 22 | @end 23 | 24 | BOOL isAllChanging = NO; 25 | 26 | @implementation AppDelegate 27 | 28 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 29 | [self configureListeners]; 30 | } 31 | 32 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 33 | // Insert code here to tear down your application 34 | [_red setStateChangeBlock:nil]; 35 | [_green setStateChangeBlock:nil]; 36 | [_blue setStateChangeBlock:nil]; 37 | [_all setStateChangeBlock:nil]; 38 | } 39 | 40 | - (void)configureListeners { 41 | [_red setStateChangeBlock:^(DSFToggleButton * _Nonnull button) { 42 | NSLog(@"Red did change to state %ld", [button state]); 43 | [self updateAllButton]; 44 | }]; 45 | [_green setStateChangeBlock:^(DSFToggleButton * _Nonnull button) { 46 | NSLog(@"Green did change to state %ld", [button state]); 47 | [self updateAllButton]; 48 | }]; 49 | [_blue setStateChangeBlock:^(DSFToggleButton * _Nonnull button) { 50 | NSLog(@"Blue did change to state %ld", [button state]); 51 | [self updateAllButton]; 52 | }]; 53 | [_all setStateChangeBlock:^(DSFToggleButton * _Nonnull button) { 54 | NSLog(@"All did change to state %ld", [button state]); 55 | }]; 56 | } 57 | 58 | - (void)updateAllButton { 59 | if ([_red state] == NSControlStateValueOn && 60 | [_green state] == NSControlStateValueOn && 61 | [_blue state] == NSControlStateValueOn) { 62 | [_all setState:NSControlStateValueOn]; 63 | } 64 | else if ([_red state] == NSControlStateValueOff && 65 | [_green state] == NSControlStateValueOff && 66 | [_blue state] == NSControlStateValueOff) { 67 | [_all setState:NSControlStateValueOff]; 68 | } 69 | } 70 | 71 | - (IBAction)clickedAll:(DSFToggleButton *)sender { 72 | [_red setState:[sender state]]; 73 | [_green setState:[sender state]]; 74 | [_blue setState:[sender state]]; 75 | } 76 | 77 | - (IBAction)toggle:(id)sender { 78 | [_red toggle]; 79 | [_green toggle]; 80 | [_blue toggle]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton_Debugger/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DSFToggleButton_Debugger 4 | // 5 | // Created by Darren Ford on 3/3/20. 6 | // Copyright © 2020 Darren Ford. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | import DSFToggleButton 12 | 13 | @NSApplicationMain 14 | class AppDelegate: NSObject, NSApplicationDelegate { 15 | @IBOutlet var window: NSWindow! 16 | @IBOutlet var bigButton: DSFToggleButton! 17 | @IBOutlet var bigButtonColorWell: NSColorWell! 18 | 19 | @IBOutlet var toggleAppearance: NSButton! 20 | 21 | var isDark: Bool { 22 | if #available(OSX 10.14, *) { 23 | return NSApp.appearance == NSAppearance(named: .darkAqua) || 24 | NSApp.appearance == NSAppearance(named: .accessibilityHighContrastDarkAqua) 25 | } else { 26 | return false 27 | } 28 | } 29 | 30 | func applicationDidFinishLaunching(_: Notification) { 31 | // Insert code here to initialize your application 32 | bigButtonColorWell.color = self.bigButton.color 33 | 34 | if #available(OSX 10.14, *) { 35 | self.toggleAppearance.isEnabled = true 36 | let ap = NSApp.effectiveAppearance 37 | if ap.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua { 38 | toggleAppearance.state = .on 39 | } 40 | } else { 41 | self.toggleAppearance.isEnabled = false 42 | } 43 | 44 | self.bigButton.stateChangeBlock = { button in 45 | Swift.print("Toggled! State is now \(button.state)") 46 | } 47 | 48 | NSColorPanel.shared.showsAlpha = true 49 | } 50 | 51 | func applicationWillTerminate(_: Notification) { 52 | // Insert code here to tear down your application 53 | } 54 | 55 | @IBAction func primaryPress(_ sender: DSFToggleButton) { 56 | Swift.print("Primary Press! State is now \(sender.state)") 57 | } 58 | 59 | @IBAction func showLabelsToggle(_ sender: NSButton) { 60 | self.bigButton.showLabels = sender.state == .on 61 | } 62 | 63 | @IBAction func colorDidChange(_ sender: NSColorWell) { 64 | self.bigButton.color = sender.color 65 | } 66 | 67 | @IBAction func enableDisable(_ sender: NSButton) { 68 | self.bigButton.isEnabled = sender.state == .off 69 | } 70 | 71 | @IBAction func toggleAnimations(_ sender: NSButton) { 72 | self.bigButton.animated = sender.state == .on 73 | } 74 | 75 | @IBAction func toggleAppearance(_ sender: NSButton) { 76 | if #available(OSX 10.14, *) { 77 | NSApp.appearance = 78 | (sender.state == .on) ? NSAppearance(named: .darkAqua) : NSAppearance(named: .aqua) 79 | } 80 | } 81 | 82 | @IBAction func toggleHighContrast(_ sender: NSButton) { 83 | self.bigButton.highContrast = sender.state == .on 84 | } 85 | 86 | @IBAction func toggleButton(_: Any) { 87 | self.bigButton.toggle() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo.xcodeproj/xcshareddata/xcschemes/All.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo.xcodeproj/xcshareddata/xcschemes/DSFToggleButton Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo.xcodeproj/xcshareddata/xcschemes/DSFToggleButton Demo Objc.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DSFToggleButton 2 | 3 | ![](https://github.com/dagronf/dagronf.github.io/raw/master/art/projects/DSFToggleButton/primary.png) 4 | 5 | A macOS NSSwitch-style toggle button scalable to any size. Supports Swift, SwiftUI and Objective-C. 6 | 7 | ![](https://img.shields.io/github/v/tag/dagronf/DSFToggleButton) ![](https://img.shields.io/badge/macOS-10.13+-red) ![](https://img.shields.io/badge/Swift-5.0+-orange.svg) 8 | ![](https://img.shields.io/badge/License-MIT-lightgrey) [![](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager) 9 | 10 | # Why 11 | 12 | Because sometimes all you want is a nice, big, very visible toggle button/checkbox. 13 | 14 | ## Adding to your project 15 | 16 | ### Swift Package Manager 17 | 18 | Add `https://github.com/dagronf/DSFToggleButton` to your project. 19 | 20 | ## Usage 21 | 22 | Since `DSFToggleButton` inherits from `NSButton`, its behaviour is the same as for a regular checkbox. You can programatically set the state or its enabled states just as you would for an `NSButton` for example, including manual bindings. 23 | 24 | The control itself does not define its size, so you can make it as big or as small as you'd like. As all the drawing is scalable, the control will look great at any size you want. 25 | 26 | ### Interface builder 27 | 28 | Drop in a new custom view into your canvas and set its class to `DSFToggleButton`. 29 | 30 | ## Configuration 31 | 32 | | Variable | Type | Description | 33 | |----------------|-----------|------------------------------------------------------------------------------------| 34 | | `color` | `NSColor` | The color of the button when the state is on, defaults to the off background color | 35 | | `showLabels` | `Bool` | Show labels (0 and 1) on the button to increase visual distinction between states | 36 | | `animated` | `Bool` | Whether to animate state changes | 37 | | `highContrast` | `Bool` | Remove any visual flourishes on the control | 38 | 39 | | Callback | Description | 40 | |-----------|-----------------------------------------------------------------------------------| 41 | | `stateChangeBlock` | A block-based callback mechanism (optional) which will be called whenever the button state changes | 42 | 43 | # Screenshots 44 | 45 | ### Dark mode, green (no labels) 46 | 47 | ![](https://github.com/dagronf/dagronf.github.io/raw/master/art/projects/DSFToggleButton/green_toggle.gif) 48 | 49 | ### Dark mode, blue with labels 50 | 51 | ![](https://github.com/dagronf/dagronf.github.io/raw/master/art/projects/DSFToggleButton/blue_toggle_labels.gif) 52 | 53 | ### Light mode with labels, high contrast 54 | 55 | ![](https://github.com/dagronf/dagronf.github.io/raw/master/art/projects/DSFToggleButton/gray_toggle_high_contrast.gif) 56 | 57 | ### Interface Builder 58 | 59 | ![](https://github.com/dagronf/dagronf.github.io/raw/master/art/projects/DSFToggleButton/interface_builder.png) 60 | 61 | # License 62 | 63 | ``` 64 | MIT License 65 | 66 | Copyright (c) 2024 Darren Ford 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining a copy 69 | of this software and associated documentation files (the "Software"), to deal 70 | in the Software without restriction, including without limitation the rights 71 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 72 | copies of the Software, and to permit persons to whom the Software is 73 | furnished to do so, subject to the following conditions: 74 | 75 | The above copyright notice and this permission notice shall be included in all 76 | copies or substantial portions of the Software. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 79 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 80 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 81 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 82 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 83 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 84 | SOFTWARE. 85 | ``` 86 | -------------------------------------------------------------------------------- /Sources/DSFToggleButton/DSFToggleButton+extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFToggleButton+extensions.swift 3 | // 4 | // Copyright © 2024 Darren Ford. All rights reserved. 5 | // 6 | // MIT License 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | #if os(macOS) 27 | 28 | import AppKit 29 | 30 | internal extension BinaryFloatingPoint { 31 | /// Round a floating point value to the nearest 0.5 32 | func toNP5() -> Self { 33 | var result = self.rounded(.towardZero) 34 | let diff = self - result 35 | if diff > 0.5 { 36 | result += self > 0 ? 0.5 : -0.5 37 | } 38 | return result 39 | } 40 | } 41 | 42 | internal extension NSRect { 43 | /// Return a tweaked rect where all edges sit on a multiple of 0.5 44 | func toNP5() -> CGRect { 45 | return CGRect( 46 | x: self.origin.x.toNP5(), 47 | y: self.origin.y.toNP5(), 48 | width: self.size.width.toNP5(), 49 | height: self.size.height.toNP5() 50 | ) 51 | } 52 | } 53 | 54 | // MARK: - NSColor extension 55 | 56 | internal extension NSColor { 57 | /// Returns a black or white contrasting color for this color 58 | /// - Parameter defaultColor: If the color cannot be converted to the genericRGB colorspace, or the input color is .clear, the fallback color 59 | /// - Returns: black or white depending on which provides the greatest contrast to this color 60 | func flatContrastColor(defaultColor: NSColor = .textColor) -> NSColor { 61 | guard self != .clear, let rgba = RGBAColor(self) else { 62 | return defaultColor 63 | } 64 | 65 | // Counting the perceptive luminance - human eye favors green color... 66 | let avgGray: CGFloat = 1 - (0.299 * rgba.R + 0.587 * rgba.G + 0.114 * rgba.B) 67 | return avgGray > 0.45 ? .white : .black 68 | } 69 | 70 | /// A simple struct to help with conversions of colors to RGBA colorspace 71 | private struct RGBAColor { 72 | let R: CGFloat 73 | let G: CGFloat 74 | let B: CGFloat 75 | let A: CGFloat 76 | let rgbaColor: NSColor 77 | 78 | init?(_ color: NSColor) { 79 | guard let c = color.usingColorSpace(.deviceRGB) else { return nil } 80 | self.R = c.redComponent 81 | self.G = c.greenComponent 82 | self.B = c.blueComponent 83 | self.A = c.alphaComponent 84 | self.rgbaColor = c 85 | } 86 | } 87 | 88 | /// Returns a color that is the result of THIS color applied on top of `backgroundColor` 89 | /// taking into account transparencies 90 | /// 91 | /// [Wikipedia Entry defining the algorithm](https://en.wikipedia.org/wiki/Alpha_compositing) 92 | /// (Refer to the section "Analytical derivation of the over operator" for derivation of these formulas) 93 | /// 94 | /// [Stack Overflow implementation here](https://stackoverflow.com/questions/726549/algorithm-for-additive-color-mixing-for-rgb-values) 95 | func applyOnTopOf(_ backgroundColor: NSColor) -> NSColor { 96 | guard let fg = RGBAColor(self), let bg = RGBAColor(backgroundColor) else { 97 | return self 98 | } 99 | 100 | let rA: CGFloat = 1 - (1 - fg.A) * (1 - bg.A) 101 | if rA < 1.0e-6 { 102 | return .clear // Fully transparent -- R,G,B not important 103 | } 104 | 105 | let rR: CGFloat = fg.R * fg.A / rA + bg.R * bg.A * (1 - fg.A) / rA 106 | let rG: CGFloat = fg.G * fg.A / rA + bg.G * bg.A * (1 - fg.A) / rA 107 | let rB: CGFloat = fg.B * fg.A / rA + bg.B * bg.A * (1 - fg.A) / rA 108 | 109 | return NSColor(calibratedRed: rR, green: rG, blue: rB, alpha: rA) 110 | } 111 | 112 | /// Returns an inverted version this color, optionally preserving the colorspace from the original color if possible 113 | func inverted(preserveColorSpace: Bool = false) -> NSColor { 114 | guard let rgbColor = RGBAColor(self) else { 115 | return self 116 | } 117 | let inverted = NSColor( 118 | calibratedRed: 1.0 - rgbColor.R, 119 | green: 1.0 - rgbColor.G, 120 | blue: 1.0 - rgbColor.B, 121 | alpha: rgbColor.A 122 | ) 123 | if preserveColorSpace, let c2 = inverted.usingColorSpace(self.colorSpace) { 124 | return c2 125 | } 126 | return inverted 127 | } 128 | } 129 | 130 | /// Perform an immediate `transform` of a given `subject`. The `transform` 131 | /// function may just mutate the given `subject`, or replace it entirely. 132 | /// 133 | /// ``` 134 | /// let oneAndTwo = mutate([1]) { 135 | /// $0.append(2) 136 | /// } 137 | /// ``` 138 | /// 139 | /// - Parameters: 140 | /// - subject: The subject to transform. 141 | /// - transform: The transformation to perform. 142 | /// 143 | /// - Throws: Any error that was thrown inside `transform`. 144 | /// 145 | /// - Returns: A transformed `subject`. 146 | @discardableResult 147 | @inlinable internal func with(_ subject: T, _ transform: (_ subject: inout T) throws -> Void) rethrows -> T { 148 | var subject = subject 149 | try transform(&subject) 150 | return subject 151 | } 152 | 153 | #endif 154 | -------------------------------------------------------------------------------- /Sources/DSFToggleButton/DSFToggleButton+SwiftUI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFToggleButton+SwiftUI.swift 3 | // 4 | // Copyright © 2024 Darren Ford. All rights reserved. 5 | // 6 | // MIT License 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | // Only works for SwiftUI on macOS 27 | #if canImport(SwiftUI) && os(macOS) 28 | 29 | import SwiftUI 30 | 31 | @available(macOS 11, *) 32 | public extension DSFToggleButton { 33 | struct SwiftUI { 34 | let controlColor: CGColor 35 | 36 | /// The current state of the button 37 | let state: Binding 38 | 39 | /// Enable or disable the control 40 | let isEnabled: Bool 41 | 42 | /// Called when the user interacts with the control 43 | let action: ((NSControl.StateValue) -> Void)? 44 | 45 | /// Called regardless of where the change came from (ie. programatically OR user-initiated) 46 | let stateChanged: ((NSControl.StateValue) -> Void)? 47 | 48 | /// Show the labels on the button 49 | let showLabels: Bool 50 | 51 | /// Force high-contrast color scheme 52 | let forceHighContrast: Bool 53 | 54 | public static let DefaultColor: CGColor = NSColor.controlAccentColor.cgColor 55 | 56 | public func makeCoordinator() -> Coordinator { 57 | Coordinator(self) 58 | } 59 | 60 | public init( 61 | state: Binding, 62 | isEnabled: Bool = true, 63 | controlColor: CGColor = Self.DefaultColor, 64 | showLabels: Bool = false, 65 | forceHighContrast: Bool = false, 66 | action: ((NSControl.StateValue) -> Void)? = nil, 67 | stateChanged: ((NSControl.StateValue) -> Void)? = nil 68 | ) { 69 | self.state = state 70 | self.isEnabled = isEnabled 71 | self.controlColor = controlColor 72 | self.showLabels = showLabels 73 | self.forceHighContrast = forceHighContrast 74 | self.action = action 75 | self.stateChanged = stateChanged 76 | } 77 | } 78 | } 79 | 80 | @available(macOS 11, *) 81 | public extension DSFToggleButton.SwiftUI { 82 | class Coordinator: NSObject { 83 | let parent: DSFToggleButton.SwiftUI 84 | var observer: NSKeyValueObservation? 85 | var button: DSFToggleButton? { 86 | didSet { 87 | self.observer = self.button?.observe(\.state, options: [.new], changeHandler: { [weak self] obj, change in 88 | self?.parent.stateChanged?(change.newValue ?? .off) 89 | }) 90 | } 91 | } 92 | 93 | init(_ toggle: DSFToggleButton.SwiftUI) { 94 | self.parent = toggle 95 | } 96 | 97 | deinit { 98 | self.observer = nil 99 | self.button?.target = nil 100 | } 101 | 102 | @objc func buttonPressed(_ sender: AnyObject) { 103 | let newVal = self.button?.state ?? .off 104 | 105 | // Update our internal state so that it's reflected up through the binding 106 | self.parent.state.wrappedValue = newVal 107 | 108 | // Notify any blocks that might be listening 109 | self.parent.action?(newVal) 110 | self.parent.stateChanged?(newVal) 111 | } 112 | } 113 | } 114 | 115 | @available(macOS 11, *) 116 | extension DSFToggleButton.SwiftUI: NSViewRepresentable { 117 | public typealias NSViewType = DSFToggleButton 118 | 119 | public func makeNSView(context: Context) -> DSFToggleButton { 120 | let button = DSFToggleButton(frame: .zero) 121 | button.translatesAutoresizingMaskIntoConstraints = false 122 | 123 | button.target = context.coordinator 124 | button.action = #selector(Coordinator.buttonPressed(_:)) 125 | 126 | button.showLabels = self.showLabels 127 | button.highContrast = self.forceHighContrast 128 | 129 | context.coordinator.button = button 130 | 131 | return button 132 | } 133 | 134 | public func updateNSView(_ nsView: DSFToggleButton, context: Context) { 135 | if nsView.state != self.state.wrappedValue { 136 | nsView.state = self.state.wrappedValue 137 | } 138 | if self.controlColor != nsView.color.cgColor { 139 | nsView.color = NSColor(cgColor: self.controlColor) ?? NSColor.controlAccentColor 140 | } 141 | 142 | if nsView.isEnabled != self.isEnabled { 143 | nsView.isEnabled = self.isEnabled 144 | } 145 | 146 | if nsView.showLabels != self.showLabels { 147 | nsView.showLabels = self.showLabels 148 | } 149 | 150 | if nsView.highContrast != self.forceHighContrast { 151 | nsView.highContrast = self.forceHighContrast 152 | } 153 | } 154 | } 155 | 156 | #if DEBUG 157 | 158 | @available(macOS 11, *) 159 | struct ToggleButtonDocoPreviews: PreviewProvider { 160 | static var previews: some View { 161 | VStack { 162 | HStack { 163 | DSFToggleButton.SwiftUI( 164 | state: .constant(.off), 165 | controlColor: NSColor.systemGreen.cgColor 166 | ) 167 | .frame(width: 150) 168 | DSFToggleButton.SwiftUI( 169 | state: .constant(.on), 170 | controlColor: NSColor.systemGreen.cgColor 171 | ) 172 | .frame(width: 150) 173 | } 174 | 175 | HStack { 176 | DSFToggleButton.SwiftUI( 177 | state: .constant(.off), 178 | controlColor: NSColor.systemGreen.cgColor, 179 | showLabels: true 180 | ) 181 | .frame(width: 150) 182 | DSFToggleButton.SwiftUI( 183 | state: .constant(.on), 184 | controlColor: NSColor.red.cgColor, 185 | showLabels: true 186 | ) 187 | .frame(width: 150) 188 | } 189 | 190 | HStack { 191 | DSFToggleButton.SwiftUI( 192 | state: .constant(.off), 193 | controlColor: NSColor.systemGreen.cgColor, 194 | showLabels: true, 195 | forceHighContrast: true 196 | ) 197 | .frame(width: 150) 198 | DSFToggleButton.SwiftUI( 199 | state: .constant(.on), 200 | controlColor: NSColor.systemPurple.cgColor, 201 | showLabels: true, 202 | forceHighContrast: true 203 | ) 204 | .frame(width: 150) 205 | } 206 | } 207 | .padding(20) 208 | .frame(width: 400, height: 300) 209 | } 210 | } 211 | 212 | 213 | @available(macOS 11, *) 214 | struct ToggleButtonPreviews: PreviewProvider { 215 | static var previews: some View { 216 | VStack { 217 | HStack { 218 | DSFToggleButton.SwiftUI( 219 | state: .constant(.off), 220 | controlColor: NSColor.systemGreen.cgColor 221 | ) 222 | DSFToggleButton.SwiftUI( 223 | state: .constant(.on), 224 | controlColor: NSColor.systemGreen.cgColor 225 | ) 226 | } 227 | HStack { 228 | DSFToggleButton.SwiftUI( 229 | state: .constant(.off), 230 | isEnabled: false, 231 | controlColor: NSColor.systemGreen.cgColor 232 | ) 233 | DSFToggleButton.SwiftUI( 234 | state: .constant(.on), 235 | isEnabled: false, 236 | controlColor: NSColor.systemGreen.cgColor 237 | ) 238 | } 239 | HStack { 240 | DSFToggleButton.SwiftUI( 241 | state: .constant(.off), 242 | controlColor: NSColor.systemPurple.cgColor, 243 | showLabels: true 244 | ) 245 | DSFToggleButton.SwiftUI( 246 | state: .constant(.on), 247 | controlColor: NSColor.systemRed.cgColor, 248 | showLabels: true 249 | ) 250 | } 251 | HStack { 252 | DSFToggleButton.SwiftUI( 253 | state: .constant(.off), 254 | isEnabled: false, 255 | controlColor: NSColor.systemPurple.cgColor, 256 | showLabels: true 257 | ) 258 | DSFToggleButton.SwiftUI( 259 | state: .constant(.on), 260 | isEnabled: false, 261 | controlColor: NSColor.systemRed.cgColor, 262 | showLabels: true 263 | ) 264 | } 265 | } 266 | } 267 | } 268 | #endif 269 | 270 | 271 | #endif 272 | -------------------------------------------------------------------------------- /Sources/DSFToggleButton/DSFToggleButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFToggleButton.swift 3 | // 4 | // Copyright © 2024 Darren Ford. All rights reserved. 5 | // 6 | // MIT License 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | #if !os(macOS) 27 | #error("DSFToggleButton only supported on macOS") 28 | #endif 29 | 30 | #if os(macOS) 31 | 32 | import AppKit 33 | import DSFAppearanceManager 34 | 35 | @IBDesignable 36 | public class DSFToggleButton: NSButton { 37 | // MARK: Public vars 38 | 39 | // All coordinates are designed in flipped coordinates 40 | override public var isFlipped: Bool { 41 | return false 42 | } 43 | 44 | /// Show labels (0 and 1) on the button to increase visual distinction between states 45 | @IBInspectable public dynamic var showLabels: Bool = false { 46 | didSet { 47 | self.needsDisplay = true 48 | } 49 | } 50 | 51 | /// The color of the button when the state is on 52 | @IBInspectable public dynamic var color: NSColor { 53 | didSet { 54 | self.needsDisplay = true 55 | } 56 | } 57 | 58 | /// Used in the interface builder to indicate whether the initial state of a button is on 59 | @IBInspectable public dynamic var isOn: Bool { 60 | didSet { 61 | self.state = self.isOn ? .on : .off 62 | } 63 | } 64 | 65 | /// Force high-contrast drawing 66 | @IBInspectable public dynamic var highContrast: Bool = false { 67 | didSet { 68 | self.needsDisplay = true 69 | } 70 | } 71 | 72 | /// Is the transition on/off animated? 73 | @IBInspectable public var animated: Bool = true 74 | 75 | /// A callback block for when the button changes state 76 | /// 77 | /// Called regardless of whether the state change comes from the user (via the UI) or by code 78 | @objc public var stateChangeBlock: ((DSFToggleButton) -> Void)? 79 | 80 | // MARK: Private vars 81 | 82 | // Are we in the process of setting ourselves up? 83 | private var initialLoad = true 84 | 85 | // Default color for the control 86 | private static let defaultColor: NSColor = .underPageBackgroundColor 87 | private static let defaultInactiveColor: NSColor = .gridColor 88 | 89 | // Listen to frame changes 90 | private var frameChangeListener: NSObjectProtocol? 91 | 92 | // The layers used within the control 93 | private let borderLayer = CAShapeLayer() 94 | private let borderMaskLayer = CAShapeLayer() 95 | private let borderShadowLayer = CAShapeLayer() 96 | private let borderShadowMaskLayer = CAShapeLayer() 97 | private let borderBorderLayer = CAShapeLayer() 98 | private let toggleCircle = CAShapeLayer() 99 | private let onLayer = CAShapeLayer() 100 | private let offLayer = CAShapeLayer() 101 | 102 | // `didSet` is called when the user programatically changes the state 103 | @objc override public var state: NSControl.StateValue { 104 | didSet { 105 | self.configureForCurrentState(animated: true) 106 | self.stateChangeBlock?(self) 107 | } 108 | } 109 | 110 | // MARK: Action Tweaking 111 | 112 | // Make sure that the action is directed to us, so that we can update on press 113 | private func twiddleAction() { 114 | let actionChanged = super.action != #selector(self._action(_:)) 115 | if actionChanged { 116 | self._action = super.action 117 | super.action = #selector(self._action(_:)) 118 | } 119 | } 120 | 121 | @objc private var _action: Selector? 122 | @objc override public var action: Selector? { 123 | didSet { 124 | self.twiddleAction() 125 | } 126 | } 127 | 128 | // MARK: Target tweaking 129 | 130 | // Make sure that we remain our own target so we can update on press 131 | private func twiddleTarget() { 132 | let targetChanged = super.target !== self 133 | if targetChanged { 134 | self._target = super.target 135 | super.target = self 136 | } 137 | } 138 | 139 | private weak var _target: AnyObject? 140 | @objc override public var target: AnyObject? { 141 | didSet { 142 | self.twiddleTarget() 143 | } 144 | } 145 | 146 | // All our drawing is going to be layer based 147 | override public var wantsUpdateLayer: Bool { 148 | return true 149 | } 150 | 151 | // MARK: Init and setup 152 | 153 | override init(frame frameRect: NSRect) { 154 | self.color = DSFToggleButton.defaultColor 155 | self.isOn = false 156 | 157 | super.init(frame: frameRect) 158 | self.setup() 159 | } 160 | 161 | required init?(coder: NSCoder) { 162 | self.color = DSFToggleButton.defaultColor 163 | self.isOn = false 164 | 165 | super.init(coder: coder) 166 | self.setup() 167 | } 168 | 169 | deinit { 170 | DSFAppearanceCache.shared.deregister(self) 171 | self.stateChangeBlock = nil 172 | self.cell?.unbind(.value) 173 | } 174 | 175 | @objc public func toggle() { 176 | self.state = (self.state == .on) ? .off : .on 177 | } 178 | 179 | override public func viewDidMoveToWindow() { 180 | super.viewDidMoveToWindow() 181 | 182 | self.twiddleTargetAction() 183 | } 184 | 185 | // Update the action and target to ourselves, and set the user's action/target values 186 | // to our private variables 187 | private func twiddleTargetAction() { 188 | self.twiddleTarget() 189 | self.twiddleAction() 190 | } 191 | } 192 | 193 | extension DSFToggleButton: DSFAppearanceCacheNotifiable { 194 | private func setup() { 195 | self.wantsLayer = true 196 | 197 | guard let layer = self.layer else { fatalError("Unable to create layer?") } 198 | 199 | self.stringValue = "" 200 | self.imagePosition = .imageOnly 201 | 202 | layer.addSublayer(self.borderLayer) 203 | self.borderLayer.mask = self.borderMaskLayer 204 | 205 | layer.addSublayer(self.borderShadowLayer) 206 | self.borderShadowLayer.mask = self.borderShadowMaskLayer 207 | 208 | layer.addSublayer(self.borderBorderLayer) 209 | layer.addSublayer(self.toggleCircle) 210 | 211 | // The on and off layers are children of the border layer 212 | self.borderLayer.addSublayer(self.onLayer) 213 | self.borderLayer.addSublayer(self.offLayer) 214 | 215 | let cell = NSButtonCell() 216 | cell.isBordered = false 217 | cell.isTransparent = true 218 | cell.setButtonType(.toggle) 219 | cell.state = self.isOn ? .on : .off 220 | cell.imagePosition = .imageOnly 221 | cell.title = "" 222 | self.cell = cell 223 | 224 | // Register for appearance change updates 225 | DSFAppearanceCache.shared.register(self) 226 | 227 | // Listen for frame changes so we can reconfigure ourselves 228 | self.postsFrameChangedNotifications = true 229 | self.frameChangeListener = NotificationCenter.default.addObserver(forName: NSView.frameDidChangeNotification, object: nil, queue: nil) { [weak self] _ in 230 | guard let `self` = self else { 231 | return 232 | } 233 | CATransaction.setDisableActions(true) 234 | self.rebuildLayers() 235 | self.needsDisplay = true 236 | } 237 | 238 | self.needsDisplay = true 239 | } 240 | 241 | public func appearanceDidChange() { 242 | self.configureForCurrentState(animated: false) 243 | } 244 | 245 | // Custom action to intercept changes to the button state via the UI 246 | @objc private func _action(_ button: NSButton) { 247 | self.configureForCurrentState(animated: true) 248 | self.stateChangeBlock?(self) 249 | if let t = _target, let a = _action { 250 | _ = t.perform(a, with: button) 251 | } 252 | } 253 | } 254 | 255 | // MARK: - Interface builder and draw 256 | 257 | public extension DSFToggleButton { 258 | override func prepareForInterfaceBuilder() { 259 | self.setup() 260 | } 261 | 262 | override func updateLayer() { 263 | super.updateLayer() 264 | self.rebuildLayers() 265 | self.configureForCurrentState(animated: false) 266 | } 267 | } 268 | 269 | // A wrapper around CGPath(roundedRect:...) to avoid crashes in 10.13 where the cornerWidth/cornerHeight 270 | // is greater than the rounded corner maximum values. It appears that on later OS versions it clamps the maximum 271 | // corner values to avoid this. Likewise if the rect is empty (zero width or zero height). 272 | private func CGSafeRoundedRect(roundedRect: CGRect, cornerWidth: CGFloat, cornerHeight: CGFloat) -> CGPath { 273 | if roundedRect.isEmpty { 274 | return CGMutablePath() 275 | } 276 | return CGPath( 277 | roundedRect: roundedRect, 278 | cornerWidth: min(roundedRect.width / 2, cornerWidth), 279 | cornerHeight: min(roundedRect.height / 2, cornerHeight), 280 | transform: nil 281 | ) 282 | } 283 | 284 | extension DSFToggleButton { 285 | private func buttonOuterFrame(for cellFrame: NSRect) -> NSRect { 286 | let newFrame: NSRect! 287 | let tHeight = cellFrame.width * (26.0 / 42.0) 288 | if tHeight > cellFrame.height { 289 | let ratioSmaller = cellFrame.height / tHeight 290 | let newWidth = cellFrame.width * ratioSmaller 291 | newFrame = NSRect(x: (cellFrame.width - newWidth) / 2.0, y: 0, width: newWidth, height: cellFrame.height - 1) 292 | } 293 | else { 294 | newFrame = NSRect(x: 0, y: (cellFrame.height - tHeight) / 2.0, width: cellFrame.width, height: tHeight - 1) 295 | } 296 | return newFrame.insetBy(dx: 1, dy: 1) 297 | } 298 | 299 | #if TARGET_INTERFACE_BUILDER 300 | // Hack to show the layout within IB 301 | override public func layout() { 302 | super.layout() 303 | self.rebuildLayers() 304 | self.updateLayer() 305 | } 306 | #endif 307 | 308 | private func rebuildLayers() { 309 | // LAYERS: 310 | // Lowest 311 | // Rounded rect color 312 | // Rounded rect inner shadow 313 | // Rounded Rect border 314 | // 0 label 315 | // 1 label 316 | // Toggle button 317 | // Highest 318 | 319 | let rect = self.buttonOuterFrame(for: self.frame) 320 | let radius = rect.height / 2.0 321 | 322 | // Lowest color rounded rect 323 | 324 | with(self.borderLayer) { outer in 325 | outer.zPosition = 0 326 | outer.path = CGSafeRoundedRect(roundedRect: rect, cornerWidth: radius, cornerHeight: radius) 327 | 328 | // Update the mask 329 | self.borderMaskLayer.path = CGSafeRoundedRect(roundedRect: rect, cornerWidth: radius, cornerHeight: radius) 330 | } 331 | 332 | with(self.borderShadowLayer) { sh in 333 | // The inner shadow for the lowest rounded rect 334 | 335 | let pth = CGMutablePath() 336 | pth.addRect(rect.insetBy(dx: -10, dy: -10)) 337 | pth.addRoundedRect(in: rect, cornerWidth: radius, cornerHeight: radius) 338 | pth.closeSubpath() 339 | 340 | sh.fillRule = .evenOdd 341 | sh.fillColor = .black 342 | 343 | sh.shadowOpacity = 0.8 344 | sh.shadowColor = .black 345 | sh.shadowOffset = CGSize(width: 1, height: -1) 346 | sh.shadowRadius = radius > 12 ? 1.5 : 0.5 347 | 348 | sh.path = pth 349 | sh.strokeColor = nil 350 | sh.zPosition = 10 351 | 352 | if let shm = sh.mask as? CAShapeLayer { 353 | shm.path = CGSafeRoundedRect(roundedRect: rect, cornerWidth: radius, cornerHeight: radius) 354 | } 355 | } 356 | 357 | with(self.borderBorderLayer) { border in 358 | // Top level border for the rounded rect 359 | border.path = CGSafeRoundedRect(roundedRect: rect, cornerWidth: radius, cornerHeight: radius) 360 | border.zPosition = 20 361 | border.fillColor = nil 362 | } 363 | 364 | let r: CGFloat = radius / 1.7 365 | 366 | // Labels 367 | 368 | let lineWidth: CGFloat = max(1, ((1.0 / 8.0) * radius).toNP5()) 369 | 370 | with(self.onLayer) { onItem in 371 | 372 | // The 1 label 373 | 374 | let ll = rect.width * 0.23 375 | let leftpos: CGFloat = rect.minX + ll + ((radius < 30) ? 1.0 : 0.0) 376 | 377 | let ooo1 = NSRect(x: leftpos, y: rect.origin.y + (rect.height / 2.0) - (r / 2.0) - 0.5, 378 | width: lineWidth, height: r + 2).toNP5() 379 | 380 | onItem.path = CGSafeRoundedRect(roundedRect: ooo1, cornerWidth: lineWidth / 2, cornerHeight: lineWidth / 2) 381 | onItem.strokeColor = nil 382 | onItem.zPosition = 30 383 | } 384 | 385 | with(self.offLayer) { offItem in 386 | // The 0 label 387 | let rr = rect.width * 0.69 388 | let rightpos: CGFloat = rect.minX + rr 389 | 390 | let ooo = NSRect(x: rightpos - 1, y: rect.origin.y + (rect.height / 2.0) - (r / 2.0), width: r + 1, height: r + 1).toNP5() 391 | offItem.path = CGPath(ellipseIn: ooo, transform: nil) 392 | offItem.fillColor = .clear 393 | offItem.lineWidth = lineWidth - 0.5 394 | offItem.zPosition = 30 395 | } 396 | 397 | with(self.toggleCircle) { toggleCircle in 398 | 399 | // The toggle circle head 400 | 401 | var circle = rect 402 | circle.size.width = rect.height 403 | 404 | // Inset the circle to make it look a bit nicer 405 | let inset = max(2.5, circle.width * 0.06) 406 | 407 | toggleCircle.path = CGPath(ellipseIn: circle.insetBy(dx: inset, dy: inset), transform: nil) 408 | toggleCircle.position.x = self.state == .on ? rect.width - rect.height : 0 409 | toggleCircle.zPosition = 50 410 | 411 | toggleCircle.shadowOpacity = 0.8 412 | toggleCircle.shadowColor = .black 413 | toggleCircle.shadowOffset = NSSize(width: 1, height: -1) 414 | toggleCircle.shadowRadius = radius > 12 ? 1.5 : 0.5 415 | } 416 | 417 | self.initialLoad = false 418 | } 419 | 420 | override public func drawFocusRingMask() { 421 | let rect = self.buttonOuterFrame(for: self.frame) 422 | let radius = rect.height / 2.0 423 | NSColor.black.setFill() 424 | let rectanglePath = NSBezierPath(roundedRect: rect, xRadius: radius, yRadius: radius) 425 | rectanglePath.fill() 426 | } 427 | 428 | func configureForCurrentState(animated: Bool) { 429 | let rect = self.buttonOuterFrame(for: self.frame) 430 | let radius = rect.height / 2.0 431 | 432 | let highContrast = DSFAppearanceCache.shared.increaseContrast || self.highContrast 433 | 434 | if !animated || DSFAppearanceCache.shared.reduceMotion || self.initialLoad || !self.animated { 435 | CATransaction.setDisableActions(true) 436 | } 437 | else { 438 | CATransaction.setAnimationDuration(0.15) 439 | CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: .easeOut)) 440 | } 441 | 442 | // 'Differentiate without color' always shows the labels 443 | let showLabels = (self.showLabels || DSFAppearanceCache.shared.differentiateWithoutColor) 444 | 445 | let isOff = (self.state == .off) // || DSFAppearanceCache.shared.differentiateWithoutColor) 446 | 447 | let bgcolor: NSColor 448 | 449 | #if TARGET_INTERFACE_BUILDER 450 | bgcolor = (self.state == .off || DSFAppearanceCache.shared.differentiateWithoutColor) ? DSFToggleButton.defaultColor : self.color 451 | #else 452 | 453 | if isOff { 454 | // We're off 455 | bgcolor = DSFToggleButton.defaultColor 456 | } 457 | else { 458 | let isKeyWindow = self.window?.isKeyWindow ?? false 459 | if isKeyWindow { 460 | // Key window AND we're on! 461 | bgcolor = self.color 462 | } 463 | else { 464 | // We're not the focussed window - return the disabled control text style 465 | bgcolor = NSColor.disabledControlTextColor 466 | } 467 | } 468 | #endif 469 | 470 | let fgcolor = bgcolor.flatContrastColor() 471 | 472 | self.borderShadowLayer.isHidden = highContrast 473 | self.borderShadowLayer.shadowRadius = radius > 12 ? 1.5 : 1 474 | 475 | self.onLayer.isHidden = !showLabels 476 | self.onLayer.fillColor = fgcolor.cgColor 477 | self.offLayer.isHidden = !showLabels 478 | self.offLayer.strokeColor = fgcolor.cgColor 479 | 480 | self.onLayer.fillColor = fgcolor.cgColor 481 | self.offLayer.strokeColor = fgcolor.cgColor 482 | 483 | self.borderLayer.fillColor = bgcolor.cgColor 484 | let borderColor = highContrast ? NSColor.textColor : NSColor.controlColor 485 | self.borderBorderLayer.strokeColor = borderColor.cgColor 486 | self.borderBorderLayer.lineWidth = 1.0 487 | self.borderBorderLayer.opacity = 1.0 488 | 489 | // Toggle color 490 | let toggleFront: CGColor = .white 491 | self.alphaValue = self.isEnabled ? 1.0 : 0.4 492 | 493 | self.toggleCircle.fillColor = toggleFront 494 | self.toggleCircle.strokeColor = highContrast ? .black : .clear 495 | self.toggleCircle.lineWidth = radius > 12 ? 1 : 0.5 496 | self.toggleCircle.shadowOpacity = highContrast ? 0.0 : 0.8 497 | self.toggleCircle.shadowRadius = radius > 12 ? 1.5 : 1 498 | 499 | if self.state == .on { 500 | self.onLayer.opacity = 1 501 | self.offLayer.opacity = 0 502 | 503 | self.toggleCircle.frame.origin = CGPoint(x: rect.width - rect.height, y: 0) 504 | let a = CGAffineTransform.identity 505 | self.onLayer.setAffineTransform(a) 506 | 507 | let b = CGAffineTransform(translationX: radius * 1.3, y: 0) 508 | self.offLayer.setAffineTransform(b) 509 | } 510 | else { 511 | self.onLayer.opacity = 0 512 | self.offLayer.opacity = 1 513 | 514 | self.toggleCircle.frame.origin = CGPoint(x: 0, y: 0) 515 | 516 | let a = CGAffineTransform(translationX: -radius * 1.3, y: 0) 517 | self.onLayer.setAffineTransform(a) 518 | 519 | self.offLayer.setAffineTransform(CGAffineTransform.identity) 520 | } 521 | 522 | CATransaction.commit() 523 | } 524 | } 525 | 526 | #endif 527 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 2355F0F8254F91B7001A05E4 /* All */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 2355F0FB254F91B7001A05E4 /* Build configuration list for PBXAggregateTarget "All" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | 23A8FA032975EEB4007BA329 /* PBXTargetDependency */, 17 | 2355F0FE254F91BD001A05E4 /* PBXTargetDependency */, 18 | 2355F100254F91BD001A05E4 /* PBXTargetDependency */, 19 | 2355F102254F91BD001A05E4 /* PBXTargetDependency */, 20 | ); 21 | name = All; 22 | productName = All; 23 | }; 24 | /* End PBXAggregateTarget section */ 25 | 26 | /* Begin PBXBuildFile section */ 27 | 2314759D23FB3E7C0083CC8E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2314759C23FB3E7C0083CC8E /* AppDelegate.m */; }; 28 | 2314759F23FB3E7C0083CC8E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2314759E23FB3E7C0083CC8E /* Assets.xcassets */; }; 29 | 231475A223FB3E7C0083CC8E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 231475A023FB3E7C0083CC8E /* MainMenu.xib */; }; 30 | 231475A523FB3E7C0083CC8E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 231475A423FB3E7C0083CC8E /* main.m */; }; 31 | 2355F0F5254F918A001A05E4 /* DSFToggleButton in Frameworks */ = {isa = PBXBuildFile; productRef = 2355F0F4254F918A001A05E4 /* DSFToggleButton */; }; 32 | 236DF7F6240DB7C60091E6B1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 236DF7F5240DB7C60091E6B1 /* AppDelegate.swift */; }; 33 | 236DF7F8240DB7C70091E6B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 236DF7F7240DB7C70091E6B1 /* Assets.xcassets */; }; 34 | 236DF7FB240DB7C70091E6B1 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 236DF7F9240DB7C70091E6B1 /* MainMenu.xib */; }; 35 | 23A8F9FB2975EDDA007BA329 /* DSFToggleButton-shared in Frameworks */ = {isa = PBXBuildFile; productRef = 23A8F9FA2975EDDA007BA329 /* DSFToggleButton-shared */; }; 36 | 23A8F9FC2975EDDA007BA329 /* DSFToggleButton-shared in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 23A8F9FA2975EDDA007BA329 /* DSFToggleButton-shared */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 37 | 23A8FA002975EE1E007BA329 /* DSFToggleButton-shared in Frameworks */ = {isa = PBXBuildFile; productRef = 23A8F9FF2975EE1E007BA329 /* DSFToggleButton-shared */; }; 38 | 23A8FA012975EE1E007BA329 /* DSFToggleButton-shared in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 23A8F9FF2975EE1E007BA329 /* DSFToggleButton-shared */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 39 | 23BC8FC623F9EE6C00885D3B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23BC8FC523F9EE6C00885D3B /* AppDelegate.swift */; }; 40 | 23BC8FC823F9EE6D00885D3B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23BC8FC723F9EE6D00885D3B /* Assets.xcassets */; }; 41 | 23BC8FCB23F9EE6D00885D3B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 23BC8FC923F9EE6D00885D3B /* MainMenu.xib */; }; 42 | 23E932B425ABA99B0054A8E5 /* DSFToggleButton_SwiftUI_DemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E932B325ABA99B0054A8E5 /* DSFToggleButton_SwiftUI_DemoApp.swift */; }; 43 | 23E932B625ABA99B0054A8E5 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E932B525ABA99B0054A8E5 /* ContentView.swift */; }; 44 | 23E932B825ABA99C0054A8E5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23E932B725ABA99C0054A8E5 /* Assets.xcassets */; }; 45 | 23E932BB25ABA99C0054A8E5 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23E932BA25ABA99C0054A8E5 /* Preview Assets.xcassets */; }; 46 | 23E932C425ABAA270054A8E5 /* DSFToggleButton in Frameworks */ = {isa = PBXBuildFile; productRef = 23E932C325ABAA270054A8E5 /* DSFToggleButton */; }; 47 | /* End PBXBuildFile section */ 48 | 49 | /* Begin PBXContainerItemProxy section */ 50 | 2355F0FD254F91BD001A05E4 /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = 23BC8FBA23F9EE6C00885D3B /* Project object */; 53 | proxyType = 1; 54 | remoteGlobalIDString = 23BC8FC123F9EE6C00885D3B; 55 | remoteInfo = "DSFToggleButton Demo"; 56 | }; 57 | 2355F0FF254F91BD001A05E4 /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = 23BC8FBA23F9EE6C00885D3B /* Project object */; 60 | proxyType = 1; 61 | remoteGlobalIDString = 2314759823FB3E7C0083CC8E; 62 | remoteInfo = "DSFToggleButton Demo Objc"; 63 | }; 64 | 2355F101254F91BD001A05E4 /* PBXContainerItemProxy */ = { 65 | isa = PBXContainerItemProxy; 66 | containerPortal = 23BC8FBA23F9EE6C00885D3B /* Project object */; 67 | proxyType = 1; 68 | remoteGlobalIDString = 236DF7F2240DB7C60091E6B1; 69 | remoteInfo = DSFToggleButton_Debugger; 70 | }; 71 | 23A8FA022975EEB4007BA329 /* PBXContainerItemProxy */ = { 72 | isa = PBXContainerItemProxy; 73 | containerPortal = 23BC8FBA23F9EE6C00885D3B /* Project object */; 74 | proxyType = 1; 75 | remoteGlobalIDString = 23E932B025ABA99B0054A8E5; 76 | remoteInfo = "DSFToggleButton SwiftUI Demo"; 77 | }; 78 | /* End PBXContainerItemProxy section */ 79 | 80 | /* Begin PBXCopyFilesBuildPhase section */ 81 | 2355F0CB254F80A2001A05E4 /* Embed Frameworks */ = { 82 | isa = PBXCopyFilesBuildPhase; 83 | buildActionMask = 2147483647; 84 | dstPath = ""; 85 | dstSubfolderSpec = 10; 86 | files = ( 87 | ); 88 | name = "Embed Frameworks"; 89 | runOnlyForDeploymentPostprocessing = 0; 90 | }; 91 | 2355F0DC254F834F001A05E4 /* Embed Frameworks */ = { 92 | isa = PBXCopyFilesBuildPhase; 93 | buildActionMask = 2147483647; 94 | dstPath = ""; 95 | dstSubfolderSpec = 10; 96 | files = ( 97 | 23A8F9FC2975EDDA007BA329 /* DSFToggleButton-shared in Embed Frameworks */, 98 | ); 99 | name = "Embed Frameworks"; 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | 2355F0E5254F839C001A05E4 /* Embed Frameworks */ = { 103 | isa = PBXCopyFilesBuildPhase; 104 | buildActionMask = 2147483647; 105 | dstPath = ""; 106 | dstSubfolderSpec = 10; 107 | files = ( 108 | 23A8FA012975EE1E007BA329 /* DSFToggleButton-shared in Embed Frameworks */, 109 | ); 110 | name = "Embed Frameworks"; 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | /* End PBXCopyFilesBuildPhase section */ 114 | 115 | /* Begin PBXFileReference section */ 116 | 2314759923FB3E7C0083CC8E /* DSFToggleButton Demo Objc.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFToggleButton Demo Objc.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 117 | 2314759B23FB3E7C0083CC8E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 118 | 2314759C23FB3E7C0083CC8E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 119 | 2314759E23FB3E7C0083CC8E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 120 | 231475A123FB3E7C0083CC8E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 121 | 231475A323FB3E7C0083CC8E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 122 | 231475A423FB3E7C0083CC8E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 123 | 231475A623FB3E7C0083CC8E /* DSFToggleButton_Demo_Objc.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFToggleButton_Demo_Objc.entitlements; sourceTree = ""; }; 124 | 2355F0AA254F7D9E001A05E4 /* DSFToggleButton */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DSFToggleButton; path = ../..; sourceTree = ""; }; 125 | 236DF7F3240DB7C60091E6B1 /* DSFToggleButton_Debugger.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DSFToggleButton_Debugger.app; sourceTree = BUILT_PRODUCTS_DIR; }; 126 | 236DF7F5240DB7C60091E6B1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 127 | 236DF7F7240DB7C70091E6B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 128 | 236DF7FA240DB7C70091E6B1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 129 | 236DF7FC240DB7C70091E6B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 130 | 236DF7FD240DB7C70091E6B1 /* DSFToggleButton_Debugger.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFToggleButton_Debugger.entitlements; sourceTree = ""; }; 131 | 23BC8FC223F9EE6C00885D3B /* DSFToggleButton Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFToggleButton Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 132 | 23BC8FC523F9EE6C00885D3B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 133 | 23BC8FC723F9EE6D00885D3B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 134 | 23BC8FCA23F9EE6D00885D3B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 135 | 23BC8FCC23F9EE6D00885D3B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 136 | 23BC8FCD23F9EE6D00885D3B /* DSFToggleButton_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFToggleButton_Demo.entitlements; sourceTree = ""; }; 137 | 23E932B125ABA99B0054A8E5 /* DSFToggleButton SwiftUI Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFToggleButton SwiftUI Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 138 | 23E932B325ABA99B0054A8E5 /* DSFToggleButton_SwiftUI_DemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DSFToggleButton_SwiftUI_DemoApp.swift; sourceTree = ""; }; 139 | 23E932B525ABA99B0054A8E5 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 140 | 23E932B725ABA99C0054A8E5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 141 | 23E932BA25ABA99C0054A8E5 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 142 | 23E932BC25ABA99C0054A8E5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 143 | 23E932BD25ABA99C0054A8E5 /* DSFToggleButton_SwiftUI_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFToggleButton_SwiftUI_Demo.entitlements; sourceTree = ""; }; 144 | /* End PBXFileReference section */ 145 | 146 | /* Begin PBXFrameworksBuildPhase section */ 147 | 2314759623FB3E7C0083CC8E /* Frameworks */ = { 148 | isa = PBXFrameworksBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | 23A8FA002975EE1E007BA329 /* DSFToggleButton-shared in Frameworks */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | 236DF7F0240DB7C60091E6B1 /* Frameworks */ = { 156 | isa = PBXFrameworksBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | 23A8F9FB2975EDDA007BA329 /* DSFToggleButton-shared in Frameworks */, 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | 23BC8FBF23F9EE6C00885D3B /* Frameworks */ = { 164 | isa = PBXFrameworksBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | 2355F0F5254F918A001A05E4 /* DSFToggleButton in Frameworks */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | 23E932AE25ABA99B0054A8E5 /* Frameworks */ = { 172 | isa = PBXFrameworksBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | 23E932C425ABAA270054A8E5 /* DSFToggleButton in Frameworks */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXFrameworksBuildPhase section */ 180 | 181 | /* Begin PBXGroup section */ 182 | 2314759A23FB3E7C0083CC8E /* DSFToggleButton Demo Objc */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 2314759B23FB3E7C0083CC8E /* AppDelegate.h */, 186 | 2314759C23FB3E7C0083CC8E /* AppDelegate.m */, 187 | 2314759E23FB3E7C0083CC8E /* Assets.xcassets */, 188 | 231475A023FB3E7C0083CC8E /* MainMenu.xib */, 189 | 231475A323FB3E7C0083CC8E /* Info.plist */, 190 | 231475A423FB3E7C0083CC8E /* main.m */, 191 | 231475A623FB3E7C0083CC8E /* DSFToggleButton_Demo_Objc.entitlements */, 192 | ); 193 | path = "DSFToggleButton Demo Objc"; 194 | sourceTree = ""; 195 | }; 196 | 2355F0B3254F7DCC001A05E4 /* Frameworks */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | ); 200 | name = Frameworks; 201 | sourceTree = ""; 202 | }; 203 | 236DF7F4240DB7C60091E6B1 /* DSFToggleButton_Debugger */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 236DF7F5240DB7C60091E6B1 /* AppDelegate.swift */, 207 | 236DF7F7240DB7C70091E6B1 /* Assets.xcassets */, 208 | 236DF7F9240DB7C70091E6B1 /* MainMenu.xib */, 209 | 236DF7FC240DB7C70091E6B1 /* Info.plist */, 210 | 236DF7FD240DB7C70091E6B1 /* DSFToggleButton_Debugger.entitlements */, 211 | ); 212 | path = DSFToggleButton_Debugger; 213 | sourceTree = ""; 214 | }; 215 | 23BC8FB923F9EE6C00885D3B = { 216 | isa = PBXGroup; 217 | children = ( 218 | 2355F0AA254F7D9E001A05E4 /* DSFToggleButton */, 219 | 23BC8FC423F9EE6C00885D3B /* DSFToggleButton Demo */, 220 | 2314759A23FB3E7C0083CC8E /* DSFToggleButton Demo Objc */, 221 | 236DF7F4240DB7C60091E6B1 /* DSFToggleButton_Debugger */, 222 | 23E932B225ABA99B0054A8E5 /* DSFToggleButton SwiftUI Demo */, 223 | 23BC8FC323F9EE6C00885D3B /* Products */, 224 | 2355F0B3254F7DCC001A05E4 /* Frameworks */, 225 | ); 226 | sourceTree = ""; 227 | }; 228 | 23BC8FC323F9EE6C00885D3B /* Products */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | 23BC8FC223F9EE6C00885D3B /* DSFToggleButton Demo.app */, 232 | 2314759923FB3E7C0083CC8E /* DSFToggleButton Demo Objc.app */, 233 | 236DF7F3240DB7C60091E6B1 /* DSFToggleButton_Debugger.app */, 234 | 23E932B125ABA99B0054A8E5 /* DSFToggleButton SwiftUI Demo.app */, 235 | ); 236 | name = Products; 237 | sourceTree = ""; 238 | }; 239 | 23BC8FC423F9EE6C00885D3B /* DSFToggleButton Demo */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | 23BC8FC523F9EE6C00885D3B /* AppDelegate.swift */, 243 | 23BC8FC723F9EE6D00885D3B /* Assets.xcassets */, 244 | 23BC8FC923F9EE6D00885D3B /* MainMenu.xib */, 245 | 23BC8FCC23F9EE6D00885D3B /* Info.plist */, 246 | 23BC8FCD23F9EE6D00885D3B /* DSFToggleButton_Demo.entitlements */, 247 | ); 248 | path = "DSFToggleButton Demo"; 249 | sourceTree = ""; 250 | }; 251 | 23E932B225ABA99B0054A8E5 /* DSFToggleButton SwiftUI Demo */ = { 252 | isa = PBXGroup; 253 | children = ( 254 | 23E932B325ABA99B0054A8E5 /* DSFToggleButton_SwiftUI_DemoApp.swift */, 255 | 23E932B525ABA99B0054A8E5 /* ContentView.swift */, 256 | 23E932B725ABA99C0054A8E5 /* Assets.xcassets */, 257 | 23E932BC25ABA99C0054A8E5 /* Info.plist */, 258 | 23E932BD25ABA99C0054A8E5 /* DSFToggleButton_SwiftUI_Demo.entitlements */, 259 | 23E932B925ABA99C0054A8E5 /* Preview Content */, 260 | ); 261 | path = "DSFToggleButton SwiftUI Demo"; 262 | sourceTree = ""; 263 | }; 264 | 23E932B925ABA99C0054A8E5 /* Preview Content */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | 23E932BA25ABA99C0054A8E5 /* Preview Assets.xcassets */, 268 | ); 269 | path = "Preview Content"; 270 | sourceTree = ""; 271 | }; 272 | /* End PBXGroup section */ 273 | 274 | /* Begin PBXNativeTarget section */ 275 | 2314759823FB3E7C0083CC8E /* DSFToggleButton Demo Objc */ = { 276 | isa = PBXNativeTarget; 277 | buildConfigurationList = 231475A923FB3E7C0083CC8E /* Build configuration list for PBXNativeTarget "DSFToggleButton Demo Objc" */; 278 | buildPhases = ( 279 | 2314759523FB3E7C0083CC8E /* Sources */, 280 | 2314759623FB3E7C0083CC8E /* Frameworks */, 281 | 2314759723FB3E7C0083CC8E /* Resources */, 282 | 2355F0E5254F839C001A05E4 /* Embed Frameworks */, 283 | ); 284 | buildRules = ( 285 | ); 286 | dependencies = ( 287 | 2355F0F3254F9176001A05E4 /* PBXTargetDependency */, 288 | ); 289 | name = "DSFToggleButton Demo Objc"; 290 | packageProductDependencies = ( 291 | 23A8F9FF2975EE1E007BA329 /* DSFToggleButton-shared */, 292 | ); 293 | productName = "DSFToggleButton Demo Objc"; 294 | productReference = 2314759923FB3E7C0083CC8E /* DSFToggleButton Demo Objc.app */; 295 | productType = "com.apple.product-type.application"; 296 | }; 297 | 236DF7F2240DB7C60091E6B1 /* DSFToggleButton_Debugger */ = { 298 | isa = PBXNativeTarget; 299 | buildConfigurationList = 236DF800240DB7C70091E6B1 /* Build configuration list for PBXNativeTarget "DSFToggleButton_Debugger" */; 300 | buildPhases = ( 301 | 236DF7EF240DB7C60091E6B1 /* Sources */, 302 | 236DF7F0240DB7C60091E6B1 /* Frameworks */, 303 | 236DF7F1240DB7C60091E6B1 /* Resources */, 304 | 2355F0DC254F834F001A05E4 /* Embed Frameworks */, 305 | ); 306 | buildRules = ( 307 | ); 308 | dependencies = ( 309 | 2355F0EE254F915B001A05E4 /* PBXTargetDependency */, 310 | ); 311 | name = DSFToggleButton_Debugger; 312 | packageProductDependencies = ( 313 | 23A8F9FA2975EDDA007BA329 /* DSFToggleButton-shared */, 314 | ); 315 | productName = DSFToggleButton_Debugger; 316 | productReference = 236DF7F3240DB7C60091E6B1 /* DSFToggleButton_Debugger.app */; 317 | productType = "com.apple.product-type.application"; 318 | }; 319 | 23BC8FC123F9EE6C00885D3B /* DSFToggleButton Demo */ = { 320 | isa = PBXNativeTarget; 321 | buildConfigurationList = 23BC8FD023F9EE6D00885D3B /* Build configuration list for PBXNativeTarget "DSFToggleButton Demo" */; 322 | buildPhases = ( 323 | 23BC8FBE23F9EE6C00885D3B /* Sources */, 324 | 23BC8FBF23F9EE6C00885D3B /* Frameworks */, 325 | 23BC8FC023F9EE6C00885D3B /* Resources */, 326 | 2355F0CB254F80A2001A05E4 /* Embed Frameworks */, 327 | ); 328 | buildRules = ( 329 | ); 330 | dependencies = ( 331 | 2355F0F7254F9193001A05E4 /* PBXTargetDependency */, 332 | ); 333 | name = "DSFToggleButton Demo"; 334 | packageProductDependencies = ( 335 | 2355F0F4254F918A001A05E4 /* DSFToggleButton */, 336 | ); 337 | productName = "DSFToggleButton Demo"; 338 | productReference = 23BC8FC223F9EE6C00885D3B /* DSFToggleButton Demo.app */; 339 | productType = "com.apple.product-type.application"; 340 | }; 341 | 23E932B025ABA99B0054A8E5 /* DSFToggleButton SwiftUI Demo */ = { 342 | isa = PBXNativeTarget; 343 | buildConfigurationList = 23E932C025ABA99C0054A8E5 /* Build configuration list for PBXNativeTarget "DSFToggleButton SwiftUI Demo" */; 344 | buildPhases = ( 345 | 23E932AD25ABA99B0054A8E5 /* Sources */, 346 | 23E932AE25ABA99B0054A8E5 /* Frameworks */, 347 | 23E932AF25ABA99B0054A8E5 /* Resources */, 348 | ); 349 | buildRules = ( 350 | ); 351 | dependencies = ( 352 | ); 353 | name = "DSFToggleButton SwiftUI Demo"; 354 | packageProductDependencies = ( 355 | 23E932C325ABAA270054A8E5 /* DSFToggleButton */, 356 | ); 357 | productName = "DSFToggleButton SwiftUI Demo"; 358 | productReference = 23E932B125ABA99B0054A8E5 /* DSFToggleButton SwiftUI Demo.app */; 359 | productType = "com.apple.product-type.application"; 360 | }; 361 | /* End PBXNativeTarget section */ 362 | 363 | /* Begin PBXProject section */ 364 | 23BC8FBA23F9EE6C00885D3B /* Project object */ = { 365 | isa = PBXProject; 366 | attributes = { 367 | LastSwiftUpdateCheck = 1230; 368 | LastUpgradeCheck = 1130; 369 | ORGANIZATIONNAME = "Darren Ford"; 370 | TargetAttributes = { 371 | 2314759823FB3E7C0083CC8E = { 372 | CreatedOnToolsVersion = 11.3.1; 373 | }; 374 | 2355F0F8254F91B7001A05E4 = { 375 | CreatedOnToolsVersion = 12.1; 376 | }; 377 | 236DF7F2240DB7C60091E6B1 = { 378 | CreatedOnToolsVersion = 11.3.1; 379 | }; 380 | 23BC8FC123F9EE6C00885D3B = { 381 | CreatedOnToolsVersion = 11.3.1; 382 | }; 383 | 23E932B025ABA99B0054A8E5 = { 384 | CreatedOnToolsVersion = 12.3; 385 | }; 386 | }; 387 | }; 388 | buildConfigurationList = 23BC8FBD23F9EE6C00885D3B /* Build configuration list for PBXProject "DSFToggleButton Demo" */; 389 | compatibilityVersion = "Xcode 9.3"; 390 | developmentRegion = en; 391 | hasScannedForEncodings = 0; 392 | knownRegions = ( 393 | en, 394 | Base, 395 | ); 396 | mainGroup = 23BC8FB923F9EE6C00885D3B; 397 | productRefGroup = 23BC8FC323F9EE6C00885D3B /* Products */; 398 | projectDirPath = ""; 399 | projectRoot = ""; 400 | targets = ( 401 | 23BC8FC123F9EE6C00885D3B /* DSFToggleButton Demo */, 402 | 2314759823FB3E7C0083CC8E /* DSFToggleButton Demo Objc */, 403 | 236DF7F2240DB7C60091E6B1 /* DSFToggleButton_Debugger */, 404 | 2355F0F8254F91B7001A05E4 /* All */, 405 | 23E932B025ABA99B0054A8E5 /* DSFToggleButton SwiftUI Demo */, 406 | ); 407 | }; 408 | /* End PBXProject section */ 409 | 410 | /* Begin PBXResourcesBuildPhase section */ 411 | 2314759723FB3E7C0083CC8E /* Resources */ = { 412 | isa = PBXResourcesBuildPhase; 413 | buildActionMask = 2147483647; 414 | files = ( 415 | 2314759F23FB3E7C0083CC8E /* Assets.xcassets in Resources */, 416 | 231475A223FB3E7C0083CC8E /* MainMenu.xib in Resources */, 417 | ); 418 | runOnlyForDeploymentPostprocessing = 0; 419 | }; 420 | 236DF7F1240DB7C60091E6B1 /* Resources */ = { 421 | isa = PBXResourcesBuildPhase; 422 | buildActionMask = 2147483647; 423 | files = ( 424 | 236DF7F8240DB7C70091E6B1 /* Assets.xcassets in Resources */, 425 | 236DF7FB240DB7C70091E6B1 /* MainMenu.xib in Resources */, 426 | ); 427 | runOnlyForDeploymentPostprocessing = 0; 428 | }; 429 | 23BC8FC023F9EE6C00885D3B /* Resources */ = { 430 | isa = PBXResourcesBuildPhase; 431 | buildActionMask = 2147483647; 432 | files = ( 433 | 23BC8FC823F9EE6D00885D3B /* Assets.xcassets in Resources */, 434 | 23BC8FCB23F9EE6D00885D3B /* MainMenu.xib in Resources */, 435 | ); 436 | runOnlyForDeploymentPostprocessing = 0; 437 | }; 438 | 23E932AF25ABA99B0054A8E5 /* Resources */ = { 439 | isa = PBXResourcesBuildPhase; 440 | buildActionMask = 2147483647; 441 | files = ( 442 | 23E932BB25ABA99C0054A8E5 /* Preview Assets.xcassets in Resources */, 443 | 23E932B825ABA99C0054A8E5 /* Assets.xcassets in Resources */, 444 | ); 445 | runOnlyForDeploymentPostprocessing = 0; 446 | }; 447 | /* End PBXResourcesBuildPhase section */ 448 | 449 | /* Begin PBXSourcesBuildPhase section */ 450 | 2314759523FB3E7C0083CC8E /* Sources */ = { 451 | isa = PBXSourcesBuildPhase; 452 | buildActionMask = 2147483647; 453 | files = ( 454 | 231475A523FB3E7C0083CC8E /* main.m in Sources */, 455 | 2314759D23FB3E7C0083CC8E /* AppDelegate.m in Sources */, 456 | ); 457 | runOnlyForDeploymentPostprocessing = 0; 458 | }; 459 | 236DF7EF240DB7C60091E6B1 /* Sources */ = { 460 | isa = PBXSourcesBuildPhase; 461 | buildActionMask = 2147483647; 462 | files = ( 463 | 236DF7F6240DB7C60091E6B1 /* AppDelegate.swift in Sources */, 464 | ); 465 | runOnlyForDeploymentPostprocessing = 0; 466 | }; 467 | 23BC8FBE23F9EE6C00885D3B /* Sources */ = { 468 | isa = PBXSourcesBuildPhase; 469 | buildActionMask = 2147483647; 470 | files = ( 471 | 23BC8FC623F9EE6C00885D3B /* AppDelegate.swift in Sources */, 472 | ); 473 | runOnlyForDeploymentPostprocessing = 0; 474 | }; 475 | 23E932AD25ABA99B0054A8E5 /* Sources */ = { 476 | isa = PBXSourcesBuildPhase; 477 | buildActionMask = 2147483647; 478 | files = ( 479 | 23E932B625ABA99B0054A8E5 /* ContentView.swift in Sources */, 480 | 23E932B425ABA99B0054A8E5 /* DSFToggleButton_SwiftUI_DemoApp.swift in Sources */, 481 | ); 482 | runOnlyForDeploymentPostprocessing = 0; 483 | }; 484 | /* End PBXSourcesBuildPhase section */ 485 | 486 | /* Begin PBXTargetDependency section */ 487 | 2355F0EE254F915B001A05E4 /* PBXTargetDependency */ = { 488 | isa = PBXTargetDependency; 489 | productRef = 2355F0ED254F915B001A05E4 /* DSFToggleButton-Dynamic */; 490 | }; 491 | 2355F0F3254F9176001A05E4 /* PBXTargetDependency */ = { 492 | isa = PBXTargetDependency; 493 | productRef = 2355F0F2254F9176001A05E4 /* DSFToggleButton-Dynamic */; 494 | }; 495 | 2355F0F7254F9193001A05E4 /* PBXTargetDependency */ = { 496 | isa = PBXTargetDependency; 497 | productRef = 2355F0F6254F9193001A05E4 /* DSFToggleButton */; 498 | }; 499 | 2355F0FE254F91BD001A05E4 /* PBXTargetDependency */ = { 500 | isa = PBXTargetDependency; 501 | target = 23BC8FC123F9EE6C00885D3B /* DSFToggleButton Demo */; 502 | targetProxy = 2355F0FD254F91BD001A05E4 /* PBXContainerItemProxy */; 503 | }; 504 | 2355F100254F91BD001A05E4 /* PBXTargetDependency */ = { 505 | isa = PBXTargetDependency; 506 | target = 2314759823FB3E7C0083CC8E /* DSFToggleButton Demo Objc */; 507 | targetProxy = 2355F0FF254F91BD001A05E4 /* PBXContainerItemProxy */; 508 | }; 509 | 2355F102254F91BD001A05E4 /* PBXTargetDependency */ = { 510 | isa = PBXTargetDependency; 511 | target = 236DF7F2240DB7C60091E6B1 /* DSFToggleButton_Debugger */; 512 | targetProxy = 2355F101254F91BD001A05E4 /* PBXContainerItemProxy */; 513 | }; 514 | 23A8FA032975EEB4007BA329 /* PBXTargetDependency */ = { 515 | isa = PBXTargetDependency; 516 | target = 23E932B025ABA99B0054A8E5 /* DSFToggleButton SwiftUI Demo */; 517 | targetProxy = 23A8FA022975EEB4007BA329 /* PBXContainerItemProxy */; 518 | }; 519 | /* End PBXTargetDependency section */ 520 | 521 | /* Begin PBXVariantGroup section */ 522 | 231475A023FB3E7C0083CC8E /* MainMenu.xib */ = { 523 | isa = PBXVariantGroup; 524 | children = ( 525 | 231475A123FB3E7C0083CC8E /* Base */, 526 | ); 527 | name = MainMenu.xib; 528 | sourceTree = ""; 529 | }; 530 | 236DF7F9240DB7C70091E6B1 /* MainMenu.xib */ = { 531 | isa = PBXVariantGroup; 532 | children = ( 533 | 236DF7FA240DB7C70091E6B1 /* Base */, 534 | ); 535 | name = MainMenu.xib; 536 | sourceTree = ""; 537 | }; 538 | 23BC8FC923F9EE6D00885D3B /* MainMenu.xib */ = { 539 | isa = PBXVariantGroup; 540 | children = ( 541 | 23BC8FCA23F9EE6D00885D3B /* Base */, 542 | ); 543 | name = MainMenu.xib; 544 | sourceTree = ""; 545 | }; 546 | /* End PBXVariantGroup section */ 547 | 548 | /* Begin XCBuildConfiguration section */ 549 | 231475A723FB3E7C0083CC8E /* Debug */ = { 550 | isa = XCBuildConfiguration; 551 | buildSettings = { 552 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 553 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 554 | CODE_SIGN_ENTITLEMENTS = "DSFToggleButton Demo Objc/DSFToggleButton_Demo_Objc.entitlements"; 555 | CODE_SIGN_IDENTITY = "Apple Development"; 556 | CODE_SIGN_STYLE = Automatic; 557 | COMBINE_HIDPI_IMAGES = YES; 558 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 559 | ENABLE_HARDENED_RUNTIME = YES; 560 | INFOPLIST_FILE = "DSFToggleButton Demo Objc/Info.plist"; 561 | LD_RUNPATH_SEARCH_PATHS = ( 562 | "$(inherited)", 563 | "@executable_path/../Frameworks", 564 | ); 565 | MACOSX_DEPLOYMENT_TARGET = 10.13; 566 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFToggleButton-Demo-Objc"; 567 | PRODUCT_NAME = "$(TARGET_NAME)"; 568 | SWIFT_VERSION = 5.0; 569 | }; 570 | name = Debug; 571 | }; 572 | 231475A823FB3E7C0083CC8E /* Release */ = { 573 | isa = XCBuildConfiguration; 574 | buildSettings = { 575 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 576 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 577 | CODE_SIGN_ENTITLEMENTS = "DSFToggleButton Demo Objc/DSFToggleButton_Demo_Objc.entitlements"; 578 | CODE_SIGN_IDENTITY = "Apple Development"; 579 | CODE_SIGN_STYLE = Automatic; 580 | COMBINE_HIDPI_IMAGES = YES; 581 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 582 | ENABLE_HARDENED_RUNTIME = YES; 583 | INFOPLIST_FILE = "DSFToggleButton Demo Objc/Info.plist"; 584 | LD_RUNPATH_SEARCH_PATHS = ( 585 | "$(inherited)", 586 | "@executable_path/../Frameworks", 587 | ); 588 | MACOSX_DEPLOYMENT_TARGET = 10.13; 589 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFToggleButton-Demo-Objc"; 590 | PRODUCT_NAME = "$(TARGET_NAME)"; 591 | SWIFT_VERSION = 5.0; 592 | }; 593 | name = Release; 594 | }; 595 | 2355F0F9254F91B7001A05E4 /* Debug */ = { 596 | isa = XCBuildConfiguration; 597 | buildSettings = { 598 | CODE_SIGN_STYLE = Automatic; 599 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 600 | PRODUCT_NAME = "$(TARGET_NAME)"; 601 | }; 602 | name = Debug; 603 | }; 604 | 2355F0FA254F91B7001A05E4 /* Release */ = { 605 | isa = XCBuildConfiguration; 606 | buildSettings = { 607 | CODE_SIGN_STYLE = Automatic; 608 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 609 | PRODUCT_NAME = "$(TARGET_NAME)"; 610 | }; 611 | name = Release; 612 | }; 613 | 236DF7FE240DB7C70091E6B1 /* Debug */ = { 614 | isa = XCBuildConfiguration; 615 | buildSettings = { 616 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 617 | CODE_SIGN_ENTITLEMENTS = DSFToggleButton_Debugger/DSFToggleButton_Debugger.entitlements; 618 | CODE_SIGN_IDENTITY = "Apple Development"; 619 | CODE_SIGN_STYLE = Automatic; 620 | COMBINE_HIDPI_IMAGES = YES; 621 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 622 | ENABLE_HARDENED_RUNTIME = YES; 623 | INFOPLIST_FILE = DSFToggleButton_Debugger/Info.plist; 624 | LD_RUNPATH_SEARCH_PATHS = ( 625 | "$(inherited)", 626 | "@executable_path/../Frameworks", 627 | ); 628 | MACOSX_DEPLOYMENT_TARGET = 10.13; 629 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFToggleButton-Debugger"; 630 | PRODUCT_NAME = "$(TARGET_NAME)"; 631 | SWIFT_VERSION = 5.0; 632 | }; 633 | name = Debug; 634 | }; 635 | 236DF7FF240DB7C70091E6B1 /* Release */ = { 636 | isa = XCBuildConfiguration; 637 | buildSettings = { 638 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 639 | CODE_SIGN_ENTITLEMENTS = DSFToggleButton_Debugger/DSFToggleButton_Debugger.entitlements; 640 | CODE_SIGN_IDENTITY = "Apple Development"; 641 | CODE_SIGN_STYLE = Automatic; 642 | COMBINE_HIDPI_IMAGES = YES; 643 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 644 | ENABLE_HARDENED_RUNTIME = YES; 645 | INFOPLIST_FILE = DSFToggleButton_Debugger/Info.plist; 646 | LD_RUNPATH_SEARCH_PATHS = ( 647 | "$(inherited)", 648 | "@executable_path/../Frameworks", 649 | ); 650 | MACOSX_DEPLOYMENT_TARGET = 10.13; 651 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFToggleButton-Debugger"; 652 | PRODUCT_NAME = "$(TARGET_NAME)"; 653 | SWIFT_VERSION = 5.0; 654 | }; 655 | name = Release; 656 | }; 657 | 23BC8FCE23F9EE6D00885D3B /* Debug */ = { 658 | isa = XCBuildConfiguration; 659 | buildSettings = { 660 | ALWAYS_SEARCH_USER_PATHS = NO; 661 | CLANG_ANALYZER_NONNULL = YES; 662 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 663 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 664 | CLANG_CXX_LIBRARY = "libc++"; 665 | CLANG_ENABLE_MODULES = YES; 666 | CLANG_ENABLE_OBJC_ARC = YES; 667 | CLANG_ENABLE_OBJC_WEAK = YES; 668 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 669 | CLANG_WARN_BOOL_CONVERSION = YES; 670 | CLANG_WARN_COMMA = YES; 671 | CLANG_WARN_CONSTANT_CONVERSION = YES; 672 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 673 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 674 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 675 | CLANG_WARN_EMPTY_BODY = YES; 676 | CLANG_WARN_ENUM_CONVERSION = YES; 677 | CLANG_WARN_INFINITE_RECURSION = YES; 678 | CLANG_WARN_INT_CONVERSION = YES; 679 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 680 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 681 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 682 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 683 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 684 | CLANG_WARN_STRICT_PROTOTYPES = YES; 685 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 686 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 687 | CLANG_WARN_UNREACHABLE_CODE = YES; 688 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 689 | COPY_PHASE_STRIP = NO; 690 | DEBUG_INFORMATION_FORMAT = dwarf; 691 | ENABLE_STRICT_OBJC_MSGSEND = YES; 692 | ENABLE_TESTABILITY = YES; 693 | GCC_C_LANGUAGE_STANDARD = gnu11; 694 | GCC_DYNAMIC_NO_PIC = NO; 695 | GCC_NO_COMMON_BLOCKS = YES; 696 | GCC_OPTIMIZATION_LEVEL = 0; 697 | GCC_PREPROCESSOR_DEFINITIONS = ( 698 | "DEBUG=1", 699 | "$(inherited)", 700 | ); 701 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 702 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 703 | GCC_WARN_UNDECLARED_SELECTOR = YES; 704 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 705 | GCC_WARN_UNUSED_FUNCTION = YES; 706 | GCC_WARN_UNUSED_VARIABLE = YES; 707 | MACOSX_DEPLOYMENT_TARGET = 10.10; 708 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 709 | MTL_FAST_MATH = YES; 710 | ONLY_ACTIVE_ARCH = YES; 711 | SDKROOT = macosx; 712 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 713 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 714 | }; 715 | name = Debug; 716 | }; 717 | 23BC8FCF23F9EE6D00885D3B /* Release */ = { 718 | isa = XCBuildConfiguration; 719 | buildSettings = { 720 | ALWAYS_SEARCH_USER_PATHS = NO; 721 | CLANG_ANALYZER_NONNULL = YES; 722 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 723 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 724 | CLANG_CXX_LIBRARY = "libc++"; 725 | CLANG_ENABLE_MODULES = YES; 726 | CLANG_ENABLE_OBJC_ARC = YES; 727 | CLANG_ENABLE_OBJC_WEAK = YES; 728 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 729 | CLANG_WARN_BOOL_CONVERSION = YES; 730 | CLANG_WARN_COMMA = YES; 731 | CLANG_WARN_CONSTANT_CONVERSION = YES; 732 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 733 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 734 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 735 | CLANG_WARN_EMPTY_BODY = YES; 736 | CLANG_WARN_ENUM_CONVERSION = YES; 737 | CLANG_WARN_INFINITE_RECURSION = YES; 738 | CLANG_WARN_INT_CONVERSION = YES; 739 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 740 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 741 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 742 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 743 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 744 | CLANG_WARN_STRICT_PROTOTYPES = YES; 745 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 746 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 747 | CLANG_WARN_UNREACHABLE_CODE = YES; 748 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 749 | COPY_PHASE_STRIP = NO; 750 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 751 | ENABLE_NS_ASSERTIONS = NO; 752 | ENABLE_STRICT_OBJC_MSGSEND = YES; 753 | GCC_C_LANGUAGE_STANDARD = gnu11; 754 | GCC_NO_COMMON_BLOCKS = YES; 755 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 756 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 757 | GCC_WARN_UNDECLARED_SELECTOR = YES; 758 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 759 | GCC_WARN_UNUSED_FUNCTION = YES; 760 | GCC_WARN_UNUSED_VARIABLE = YES; 761 | MACOSX_DEPLOYMENT_TARGET = 10.10; 762 | MTL_ENABLE_DEBUG_INFO = NO; 763 | MTL_FAST_MATH = YES; 764 | SDKROOT = macosx; 765 | SWIFT_COMPILATION_MODE = wholemodule; 766 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 767 | }; 768 | name = Release; 769 | }; 770 | 23BC8FD123F9EE6D00885D3B /* Debug */ = { 771 | isa = XCBuildConfiguration; 772 | buildSettings = { 773 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 774 | CODE_SIGN_ENTITLEMENTS = "DSFToggleButton Demo/DSFToggleButton_Demo.entitlements"; 775 | CODE_SIGN_IDENTITY = "-"; 776 | CODE_SIGN_STYLE = Automatic; 777 | COMBINE_HIDPI_IMAGES = YES; 778 | DEVELOPMENT_TEAM = ""; 779 | ENABLE_HARDENED_RUNTIME = YES; 780 | INFOPLIST_FILE = "DSFToggleButton Demo/Info.plist"; 781 | LD_RUNPATH_SEARCH_PATHS = ( 782 | "$(inherited)", 783 | "@executable_path/../Frameworks", 784 | ); 785 | MACOSX_DEPLOYMENT_TARGET = 10.13; 786 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFToggleButton-Demo"; 787 | PRODUCT_NAME = "$(TARGET_NAME)"; 788 | SWIFT_VERSION = 5.0; 789 | }; 790 | name = Debug; 791 | }; 792 | 23BC8FD223F9EE6D00885D3B /* Release */ = { 793 | isa = XCBuildConfiguration; 794 | buildSettings = { 795 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 796 | CODE_SIGN_ENTITLEMENTS = "DSFToggleButton Demo/DSFToggleButton_Demo.entitlements"; 797 | CODE_SIGN_IDENTITY = "-"; 798 | CODE_SIGN_STYLE = Automatic; 799 | COMBINE_HIDPI_IMAGES = YES; 800 | DEVELOPMENT_TEAM = ""; 801 | ENABLE_HARDENED_RUNTIME = YES; 802 | INFOPLIST_FILE = "DSFToggleButton Demo/Info.plist"; 803 | LD_RUNPATH_SEARCH_PATHS = ( 804 | "$(inherited)", 805 | "@executable_path/../Frameworks", 806 | ); 807 | MACOSX_DEPLOYMENT_TARGET = 10.13; 808 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFToggleButton-Demo"; 809 | PRODUCT_NAME = "$(TARGET_NAME)"; 810 | SWIFT_VERSION = 5.0; 811 | }; 812 | name = Release; 813 | }; 814 | 23E932BE25ABA99C0054A8E5 /* Debug */ = { 815 | isa = XCBuildConfiguration; 816 | buildSettings = { 817 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 818 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 819 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 820 | CODE_SIGN_ENTITLEMENTS = "DSFToggleButton SwiftUI Demo/DSFToggleButton_SwiftUI_Demo.entitlements"; 821 | CODE_SIGN_STYLE = Automatic; 822 | COMBINE_HIDPI_IMAGES = YES; 823 | DEVELOPMENT_ASSET_PATHS = "\"DSFToggleButton SwiftUI Demo/Preview Content\""; 824 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 825 | ENABLE_HARDENED_RUNTIME = YES; 826 | ENABLE_PREVIEWS = YES; 827 | INFOPLIST_FILE = "DSFToggleButton SwiftUI Demo/Info.plist"; 828 | LD_RUNPATH_SEARCH_PATHS = ( 829 | "$(inherited)", 830 | "@executable_path/../Frameworks", 831 | ); 832 | MACOSX_DEPLOYMENT_TARGET = 11.0; 833 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFToggleButton-SwiftUI-Demo"; 834 | PRODUCT_NAME = "$(TARGET_NAME)"; 835 | SWIFT_VERSION = 5.0; 836 | }; 837 | name = Debug; 838 | }; 839 | 23E932BF25ABA99C0054A8E5 /* Release */ = { 840 | isa = XCBuildConfiguration; 841 | buildSettings = { 842 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 843 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 844 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 845 | CODE_SIGN_ENTITLEMENTS = "DSFToggleButton SwiftUI Demo/DSFToggleButton_SwiftUI_Demo.entitlements"; 846 | CODE_SIGN_STYLE = Automatic; 847 | COMBINE_HIDPI_IMAGES = YES; 848 | DEVELOPMENT_ASSET_PATHS = "\"DSFToggleButton SwiftUI Demo/Preview Content\""; 849 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 850 | ENABLE_HARDENED_RUNTIME = YES; 851 | ENABLE_PREVIEWS = YES; 852 | INFOPLIST_FILE = "DSFToggleButton SwiftUI Demo/Info.plist"; 853 | LD_RUNPATH_SEARCH_PATHS = ( 854 | "$(inherited)", 855 | "@executable_path/../Frameworks", 856 | ); 857 | MACOSX_DEPLOYMENT_TARGET = 11.0; 858 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFToggleButton-SwiftUI-Demo"; 859 | PRODUCT_NAME = "$(TARGET_NAME)"; 860 | SWIFT_VERSION = 5.0; 861 | }; 862 | name = Release; 863 | }; 864 | /* End XCBuildConfiguration section */ 865 | 866 | /* Begin XCConfigurationList section */ 867 | 231475A923FB3E7C0083CC8E /* Build configuration list for PBXNativeTarget "DSFToggleButton Demo Objc" */ = { 868 | isa = XCConfigurationList; 869 | buildConfigurations = ( 870 | 231475A723FB3E7C0083CC8E /* Debug */, 871 | 231475A823FB3E7C0083CC8E /* Release */, 872 | ); 873 | defaultConfigurationIsVisible = 0; 874 | defaultConfigurationName = Release; 875 | }; 876 | 2355F0FB254F91B7001A05E4 /* Build configuration list for PBXAggregateTarget "All" */ = { 877 | isa = XCConfigurationList; 878 | buildConfigurations = ( 879 | 2355F0F9254F91B7001A05E4 /* Debug */, 880 | 2355F0FA254F91B7001A05E4 /* Release */, 881 | ); 882 | defaultConfigurationIsVisible = 0; 883 | defaultConfigurationName = Release; 884 | }; 885 | 236DF800240DB7C70091E6B1 /* Build configuration list for PBXNativeTarget "DSFToggleButton_Debugger" */ = { 886 | isa = XCConfigurationList; 887 | buildConfigurations = ( 888 | 236DF7FE240DB7C70091E6B1 /* Debug */, 889 | 236DF7FF240DB7C70091E6B1 /* Release */, 890 | ); 891 | defaultConfigurationIsVisible = 0; 892 | defaultConfigurationName = Release; 893 | }; 894 | 23BC8FBD23F9EE6C00885D3B /* Build configuration list for PBXProject "DSFToggleButton Demo" */ = { 895 | isa = XCConfigurationList; 896 | buildConfigurations = ( 897 | 23BC8FCE23F9EE6D00885D3B /* Debug */, 898 | 23BC8FCF23F9EE6D00885D3B /* Release */, 899 | ); 900 | defaultConfigurationIsVisible = 0; 901 | defaultConfigurationName = Release; 902 | }; 903 | 23BC8FD023F9EE6D00885D3B /* Build configuration list for PBXNativeTarget "DSFToggleButton Demo" */ = { 904 | isa = XCConfigurationList; 905 | buildConfigurations = ( 906 | 23BC8FD123F9EE6D00885D3B /* Debug */, 907 | 23BC8FD223F9EE6D00885D3B /* Release */, 908 | ); 909 | defaultConfigurationIsVisible = 0; 910 | defaultConfigurationName = Release; 911 | }; 912 | 23E932C025ABA99C0054A8E5 /* Build configuration list for PBXNativeTarget "DSFToggleButton SwiftUI Demo" */ = { 913 | isa = XCConfigurationList; 914 | buildConfigurations = ( 915 | 23E932BE25ABA99C0054A8E5 /* Debug */, 916 | 23E932BF25ABA99C0054A8E5 /* Release */, 917 | ); 918 | defaultConfigurationIsVisible = 0; 919 | defaultConfigurationName = Release; 920 | }; 921 | /* End XCConfigurationList section */ 922 | 923 | /* Begin XCSwiftPackageProductDependency section */ 924 | 2355F0ED254F915B001A05E4 /* DSFToggleButton-Dynamic */ = { 925 | isa = XCSwiftPackageProductDependency; 926 | productName = "DSFToggleButton-Dynamic"; 927 | }; 928 | 2355F0F2254F9176001A05E4 /* DSFToggleButton-Dynamic */ = { 929 | isa = XCSwiftPackageProductDependency; 930 | productName = "DSFToggleButton-Dynamic"; 931 | }; 932 | 2355F0F4254F918A001A05E4 /* DSFToggleButton */ = { 933 | isa = XCSwiftPackageProductDependency; 934 | productName = DSFToggleButton; 935 | }; 936 | 2355F0F6254F9193001A05E4 /* DSFToggleButton */ = { 937 | isa = XCSwiftPackageProductDependency; 938 | productName = DSFToggleButton; 939 | }; 940 | 23A8F9FA2975EDDA007BA329 /* DSFToggleButton-shared */ = { 941 | isa = XCSwiftPackageProductDependency; 942 | productName = "DSFToggleButton-shared"; 943 | }; 944 | 23A8F9FF2975EE1E007BA329 /* DSFToggleButton-shared */ = { 945 | isa = XCSwiftPackageProductDependency; 946 | productName = "DSFToggleButton-shared"; 947 | }; 948 | 23E932C325ABAA270054A8E5 /* DSFToggleButton */ = { 949 | isa = XCSwiftPackageProductDependency; 950 | productName = DSFToggleButton; 951 | }; 952 | /* End XCSwiftPackageProductDependency section */ 953 | }; 954 | rootObject = 23BC8FBA23F9EE6C00885D3B /* Project object */; 955 | } 956 | -------------------------------------------------------------------------------- /Demo/DSFToggleButton Demo/DSFToggleButton Demo Objc/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | Default 544 | 545 | 546 | 547 | 548 | 549 | 550 | Left to Right 551 | 552 | 553 | 554 | 555 | 556 | 557 | Right to Left 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | Default 569 | 570 | 571 | 572 | 573 | 574 | 575 | Left to Right 576 | 577 | 578 | 579 | 580 | 581 | 582 | Right to Left 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | --------------------------------------------------------------------------------