├── 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 | 
4 |
5 | A macOS NSSwitch-style toggle button scalable to any size. Supports Swift, SwiftUI and Objective-C.
6 |
7 |   
8 |  [](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 | 
48 |
49 | ### Dark mode, blue with labels
50 |
51 | 
52 |
53 | ### Light mode with labels, high contrast
54 |
55 | 
56 |
57 | ### Interface Builder
58 |
59 | 
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 |
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 |
--------------------------------------------------------------------------------