├── Art
└── artwork.pcvd
├── Demos
└── DSFDropFilesView Demo
│ ├── DSFDropFilesView Demo
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── folder-add.imageset
│ │ │ ├── folder-add.png
│ │ │ ├── folder-add@2x.png
│ │ │ ├── folder-add@3x.png
│ │ │ └── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── DSFDropFilesView_Demo.entitlements
│ ├── AppDelegate.swift
│ ├── Info.plist
│ └── ViewController.swift
│ ├── DSFDropFilesView ObjC Demo
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.h
│ ├── ViewController.h
│ ├── main.m
│ ├── DSFDropFilesView_ObjC_Demo.entitlements
│ ├── AppDelegate.m
│ ├── Info.plist
│ ├── ViewController.m
│ └── Base.lproj
│ │ └── Main.storyboard
│ ├── DSFDropFilesView SwiftUI Demo
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── DSFDropFilesView_SwiftUI_Demo.entitlements
│ ├── Info.plist
│ ├── AppDelegate.swift
│ ├── ContentView.swift
│ └── Base.lproj
│ │ └── Main.storyboard
│ └── DSFDropFilesView Demo.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
├── Tests
├── LinuxMain.swift
└── DSFDropFilesViewTests
│ ├── XCTestManifests.swift
│ └── DSFDropFilesViewTests.swift
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── .gitignore
├── Package.resolved
├── .github
└── workflows
│ └── swift.yml
├── Package.swift
├── LICENSE
├── Sources
└── DSFDropFilesView
│ ├── private
│ ├── DSFDropFilesView+utils.swift
│ └── DSFDropFilesView+image.swift
│ ├── DSFDropFilesView+protocol.swift
│ ├── DSFDropFilesView+SwiftUI.swift
│ └── DSFDropFilesView.swift
└── README.md
/Art/artwork.pcvd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dagronf/DSFDropFilesView/HEAD/Art/artwork.pcvd
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import DSFDropFilesViewTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += DSFDropFilesViewTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dagronf/DSFDropFilesView/HEAD/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add.png
--------------------------------------------------------------------------------
/Tests/DSFDropFilesViewTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(DSFDropFilesViewTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dagronf/DSFDropFilesView/HEAD/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add@2x.png
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dagronf/DSFDropFilesView/HEAD/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add@3x.png
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView 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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC 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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView 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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // DSFDropFilesView ObjC Demo
4 | //
5 | // Created by Darren Ford on 24/11/20.
6 | //
7 |
8 | #import
9 |
10 | @interface AppDelegate : NSObject
11 |
12 |
13 | @end
14 |
15 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | Package~.swift
7 | Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/project.xcworkspace/xcshareddata
8 | ._Package.swift
9 | Demos/DSFDropFilesView Demo/._DSFDropFilesView Demo.xcodeproj
10 | Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/._project.xcworkspace
11 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // DSFDropFilesView ObjC Demo
4 | //
5 | // Created by Darren Ford on 24/11/20.
6 | //
7 |
8 | #import
9 |
10 | @import DSFDropFilesView;
11 |
12 | @interface ViewController : NSViewController
13 |
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // DSFDropFilesView ObjC Demo
4 | //
5 | // Created by Darren Ford on 24/11/20.
6 | //
7 |
8 | #import
9 |
10 | int main(int argc, const char * argv[]) {
11 | @autoreleasepool {
12 | // Setup code that might create autoreleased objects goes here.
13 | }
14 | return NSApplicationMain(argc, argv);
15 | }
16 |
--------------------------------------------------------------------------------
/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": "fd459c8060d46d17b780a80ac43db83b444e2e13",
10 | "version": "3.6.2"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/DSFDropFilesView_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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/DSFDropFilesView_ObjC_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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/DSFDropFilesView_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 |
--------------------------------------------------------------------------------
/.github/workflows/swift.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Swift project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift
3 |
4 | name: Swift
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: macos-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Build
20 | run: swift build -v
21 | - name: Run tests
22 | run: swift test -v
23 |
--------------------------------------------------------------------------------
/Tests/DSFDropFilesViewTests/DSFDropFilesViewTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import DSFDropFilesView
3 |
4 | final class DSFDropFilesViewTests: 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(DSFDropFilesView().text, "Hello, World!")
10 | // }
11 | //
12 | // static var allTests = [
13 | // ("testExample", testExample),
14 | // ]
15 | }
16 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DSFDropFilesView Demo
4 | //
5 | // Created by Darren Ford on 27/10/20.
6 | //
7 |
8 | import Cocoa
9 |
10 | @NSApplicationMain
11 | class AppDelegate: NSObject, NSApplicationDelegate {
12 |
13 |
14 |
15 |
16 | func applicationDidFinishLaunching(_ aNotification: Notification) {
17 | // Insert code here to initialize your application
18 | }
19 |
20 | func applicationWillTerminate(_ aNotification: Notification) {
21 | // Insert code here to tear down your application
22 | }
23 |
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // DSFDropFilesView ObjC Demo
4 | //
5 | // Created by Darren Ford on 24/11/20.
6 | //
7 |
8 | #import "AppDelegate.h"
9 |
10 | @interface AppDelegate ()
11 |
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
18 | // Insert code here to initialize your application
19 | }
20 |
21 |
22 | - (void)applicationWillTerminate:(NSNotification *)aNotification {
23 | // Insert code here to tear down your application
24 | }
25 |
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "folder-add.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "folder-add@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "filename" : "folder-add@3x.png",
15 | "idiom" : "universal",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "author" : "xcode",
21 | "version" : 1
22 | },
23 | "properties" : {
24 | "template-rendering-intent" : "template"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.4
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "DSFDropFilesView",
7 | platforms: [
8 | .macOS(.v10_13)
9 | ],
10 | products: [
11 | .library(name: "DSFDropFilesView", targets: ["DSFDropFilesView"]),
12 | .library(name: "DSFDropFilesView-static", type: .static, targets: ["DSFDropFilesView"]),
13 | .library(name: "DSFDropFilesView-shared", type: .dynamic, targets: ["DSFDropFilesView"]),
14 | ],
15 | dependencies: [
16 | .package(url: "https://github.com/dagronf/DSFAppearanceManager", from: "3.5.0")
17 | ],
18 | targets: [
19 | .target(
20 | name: "DSFDropFilesView",
21 | dependencies: [
22 | .product(name: "DSFAppearanceManager", package: "DSFAppearanceManager")
23 | ]
24 | ),
25 | .testTarget(
26 | name: "DSFDropFilesViewTests",
27 | dependencies: ["DSFDropFilesView"]),
28 | ]
29 | )
30 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView 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 | NSMainStoryboardFile
26 | Main
27 | NSPrincipalClass
28 | NSApplication
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC 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 | NSMainStoryboardFile
26 | Main
27 | NSPrincipalClass
28 | NSApplication
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI 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 | NSMainStoryboardFile
26 | Main
27 | NSPrincipalClass
28 | NSApplication
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DSFDropFilesView SwiftUI Demo
4 | //
5 | // Created by Darren Ford on 23/11/20.
6 | //
7 |
8 | import Cocoa
9 | import SwiftUI
10 |
11 | @main
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | var window: NSWindow!
15 |
16 |
17 | func applicationDidFinishLaunching(_ aNotification: Notification) {
18 | // Create the SwiftUI view that provides the window contents.
19 | let contentView = ContentView()
20 |
21 | // Create the window and set the content view.
22 | window = NSWindow(
23 | contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
24 | styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
25 | backing: .buffered, defer: false)
26 | window.isReleasedWhenClosed = false
27 | window.center()
28 | window.setFrameAutosaveName("Main Window")
29 | window.contentView = NSHostingView(rootView: contentView)
30 | window.makeKeyAndOrderFront(nil)
31 | }
32 |
33 | func applicationWillTerminate(_ aNotification: Notification) {
34 | // Insert code here to tear down your application
35 | }
36 |
37 |
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView 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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC 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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView 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 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // DSFDropFilesView SwiftUI Demo
4 | //
5 | // Created by Darren Ford on 23/11/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | import DSFDropFilesView
11 |
12 | struct ContentView: View {
13 |
14 | @State private var label = "Drop On Me!"
15 | @State private var dropEnabled = true
16 |
17 | private var displayProperties =
18 | DSFDropFilesView.SwiftUI.DisplayProperties(cornerRadius: 12)
19 |
20 | var body: some View {
21 | VStack {
22 | DSFDropFilesView.SwiftUI(
23 | displayProperties: self.displayProperties,
24 | isEnabled: self.dropEnabled,
25 | allowsMultipleDrop: true,
26 | iconLabel: self.label,
27 | selectFilesLabel: "Select Files…",
28 | selectFilesButtonIsLink: true,
29 | validateFiles: { urls in
30 | return .copy
31 | },
32 | dropFiles: { urls in
33 | Swift.print("\(urls)")
34 | return true
35 | },
36 | selectFiles: {
37 | Swift.print("User clicked select files")
38 | }
39 | )
40 | .padding(4)
41 |
42 | Button("Toggle settings") {
43 | self.dropEnabled.toggle()
44 | self.label = self.dropEnabled ? "Drop On Me!" : "You Can't Drop On Me!"
45 | }
46 | .padding(8)
47 | }
48 | }
49 | }
50 |
51 |
52 | struct ContentView_Previews: PreviewProvider {
53 | static var previews: some View {
54 | ContentView()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // DSFDropFilesView ObjC Demo
4 | //
5 | // Created by Darren Ford on 24/11/20.
6 | //
7 |
8 | #import "ViewController.h"
9 |
10 |
11 | @implementation ViewController
12 |
13 | - (void)viewDidLoad {
14 | [super viewDidLoad];
15 |
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 | - (void)setRepresentedObject:(id)representedObject {
20 | [super setRepresentedObject:representedObject];
21 |
22 | // Update the view, if already loaded.
23 | }
24 |
25 | - (NSDragOperation)dropFilesView:(DSFDropFilesView *)sender validateFiles:(NSArray * _Nonnull)validateFiles {
26 | return NSDragOperationCopy;
27 | }
28 |
29 | - (BOOL)dropFilesView:(DSFDropFilesView *)sender didDropFiles:(NSArray *)files {
30 | NSLog(@"User dropped files %@", files);
31 | return true;
32 | }
33 |
34 | - (void)dropFilesViewWantsSelectFiles:(DSFDropFilesView *)sender {
35 | NSLog(@"User clicked select files");
36 |
37 | __weak typeof(self) bself = self;
38 | dispatch_async(dispatch_get_main_queue(), ^(void){
39 | [bself selectFiles];
40 | });
41 | }
42 |
43 | - (void)selectFiles {
44 | NSOpenPanel* openPanel = [NSOpenPanel openPanel];
45 |
46 | [openPanel setAllowsMultipleSelection: YES];
47 | [openPanel setCanChooseDirectories:YES];
48 | [openPanel setCanCreateDirectories:YES];
49 | [openPanel setCanChooseFiles:YES];
50 |
51 | [openPanel beginWithCompletionHandler:^(NSInteger result) {
52 | if (result == NSModalResponseOK) {
53 | NSLog(@"User dropped files %@", [openPanel URLs]);
54 | }
55 | }];
56 | }
57 |
58 | @end
59 |
--------------------------------------------------------------------------------
/Sources/DSFDropFilesView/private/DSFDropFilesView+utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DSFDropFilesView+utils.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 of this software and associated
9 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
11 | // permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all copies or substantial
14 | // portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
18 | // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | //
21 |
22 | #if os(macOS)
23 |
24 | import AppKit
25 |
26 | internal class DSFHandButton: NSButton {
27 | var wantsHandCursor: Bool = true {
28 | didSet {
29 | self.needsLayout = true
30 | }
31 | }
32 | public override func resetCursorRects() {
33 | // Add the hand cursor when we're over the button
34 | if self.wantsHandCursor && self.isEnabled {
35 | self.addCursorRect(bounds, cursor: .pointingHand)
36 | }
37 | }
38 | }
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // DSFDropFilesView Demo
4 | //
5 | // Created by Darren Ford on 27/10/20.
6 | //
7 |
8 | import Cocoa
9 | import DSFDropFilesView
10 |
11 | class ViewController: NSViewController {
12 | @IBOutlet var left: DSFDropFilesView!
13 | @IBOutlet weak var middle: DSFDropFilesView!
14 | @IBOutlet var right: DSFDropFilesView!
15 |
16 | override func viewDidLoad() {
17 | super.viewDidLoad()
18 |
19 | // Do any additional setup after loading the view.
20 | }
21 |
22 | override var representedObject: Any? {
23 | didSet {
24 | // Update the view, if already loaded.
25 | }
26 | }
27 | }
28 |
29 | extension ViewController: DSFDropFilesViewProtocol {
30 | private func isFolder(_ url: URL) -> Bool {
31 | var r = ObjCBool(false)
32 | FileManager.default.fileExists(atPath: url.path, isDirectory: &r)
33 | return r.boolValue
34 | }
35 |
36 | func dropFilesViewWantsSelectFiles(_ sender: DSFDropFilesView) {
37 | guard sender === self.left else {
38 | return
39 | }
40 |
41 | let myOpen = NSOpenPanel()
42 | myOpen.allowsMultipleSelection = true
43 | myOpen.canChooseFiles = true
44 | myOpen.canChooseDirectories = true
45 |
46 | if let w = self.view.window {
47 | myOpen.beginSheetModal(for: w) { response in
48 | if response == NSApplication.ModalResponse.OK {
49 | Swift.print(myOpen.urls)
50 | }
51 | }
52 | }
53 | }
54 |
55 | func dropFilesView(_ sender: DSFDropFilesView, validateFiles: [URL]) -> NSDragOperation {
56 | if sender === self.left {
57 | let foundInvalidImage = validateFiles.first { (url) -> Bool in
58 | guard let im = NSImage(byReferencingFile: url.path) else {
59 | // Found an invalid image
60 | return true
61 | }
62 | return !im.isValid
63 | }
64 |
65 | if let _ = foundInvalidImage {
66 | return []
67 | }
68 |
69 | return .copy
70 | }
71 | else if sender === self.right {
72 | let c = validateFiles.filter { !self.isFolder($0) }.count
73 | return c > 0 ? [] : .copy
74 | }
75 | else if sender === self.middle {
76 | return .copy
77 | }
78 | return []
79 | }
80 |
81 | func dropFilesView(_: DSFDropFilesView, didDropFiles files: [URL]) -> Bool {
82 | Swift.print(files)
83 | return true
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Sources/DSFDropFilesView/DSFDropFilesView+protocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DSFDropFilesView+protocol.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 of this software and associated
9 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
11 | // permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all copies or substantial
14 | // portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
18 | // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | //
21 |
22 | #if os(macOS)
23 |
24 | import AppKit
25 |
26 | /// Delegate protocol for a Drop Files View.
27 | @objc public protocol DSFDropFilesViewProtocol: NSObjectProtocol {
28 | /// Callback to the owner that the user has clicked the 'Select Files...' button
29 | @objc optional func dropFilesViewWantsSelectFiles(_ sender: DSFDropFilesView)
30 |
31 | /// Ask the delegate to validate the urls that the user has dragged onto the control
32 | /// - Parameters:
33 | /// - sender: The drop files view that initiated the action
34 | /// - validateFiles: The urls to be validated
35 | /// - Returns the drag operation for the dragged files. Return [] to fail the drag
36 | func dropFilesView(_ sender: DSFDropFilesView, validateFiles: [URL]) -> NSDragOperation
37 |
38 | /// The files have been dropped on the view
39 | /// - Parameters:
40 | /// - sender: The drop files view that initiated the action
41 | /// - files: The urls that were dropped
42 | /// - Returns true if the drop action was successful
43 | func dropFilesView(_ sender: DSFDropFilesView, didDropFiles files: [URL]) -> Bool
44 | }
45 |
46 | #endif
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DSFDropFilesView
2 |
3 | 
4 |
5 | A simple view class for dropping files onto. Supporting Swift, SwiftUI and Objective-C.
6 |
7 |   
8 |  [](https://swift.org/package-manager)
9 |
10 |
11 | ## Why
12 |
13 | I've need something like this many times. So here's a module to make my future life easier maybe.
14 |
15 | ## Installation
16 |
17 | ### Swift Package Manager
18 |
19 | Add `https://github.com/dagronf/DSFDropFilesView` to your project.
20 |
21 | ## Usage
22 |
23 | ### Via Interface Builder
24 |
25 | Add a new `NSView` instance using Interface Builder, then change the class type to `DSFDropFilesView`
26 |
27 | ### Programatically
28 |
29 | ```swift
30 | let dropView = DSFDropFilesView(frame: .zero)
31 | ```
32 |
33 | ### SwiftUI
34 |
35 | See below.
36 |
37 | ### Objective-C
38 |
39 | Add `https://github.com/dagronf/DSFDropFilesView` to your project.
40 |
41 | Note that if you need to use `DSFDropFilesView` in Objective-C projects supporting macOS earlier than 10.14, you will also need to make sure to set `Always Embed Swift Standard Libraries` to `Yes` in the project settings. Before macOS 10.14 the Swift standard libraries are not distributed as part of the macOS installation and you'll need to provide your own in your application.
42 |
43 | ## Delegation
44 |
45 | To receive callbacks from the control, set the `dropDelegate` to your view controller.
46 |
47 | ```swift
48 | @objc optional func dropFilesViewWantsSelectFiles(_ sender: DSFDropFilesView)
49 | ```
50 | An optional method that gets called if the user clicks the 'Select files...' button on the control.
51 |
52 | ```swift
53 | func dropFilesView(_ sender: DSFDropFilesView, validateFiles: [URL]) -> NSDragOperation
54 | ```
55 | Called when the drag enters the view. Return the drag operation (or an empty array) to indicate how the drag is to proceed. Use this function to filter out if the 'wrong' types of files are dropped. (for example, if you only want to receive PDFs).
56 |
57 | ```swift
58 | func dropFilesView(_ sender: DSFDropFilesView, didDropFiles files: [URL]) -> Bool
59 | ```
60 | Called with the files when the user actually drops the files.
61 |
62 | ## Customizations
63 |
64 | ### Properties
65 |
66 | These properties can all be configured via Interface Builder or programatically.
67 |
68 | #### Allowed drop counts
69 |
70 | * `allowsMultipleDrop` : Allow multiple files/folders to dropped
71 |
72 | #### Select files button (optional)
73 |
74 | * `selectFilesButtonLabel` : Embed a clickable button for accessibility with the specified label. If empty, doesn't display the button (default)
75 | * `selectFilesButtonIsLink` : If the select files button is display, show the button as a hyperlink (blue underlined text) instead of a button.
76 |
77 | #### Icon
78 |
79 | * `showIcon` : Should we display an icon
80 | * `icon` : The icon to display. A default icon is supplied
81 |
82 | #### Label
83 |
84 | * `label` : The text of the label. If empty, the label is hidden.
85 | * `lineWidth` : The line width for the dotted border
86 | * `cornerWidth` : The radius for the corners
87 |
88 | ## SwiftUI
89 |
90 | ### SwiftUI
91 |
92 | There is a basic SwiftUI View that embeds the `DSFDropFilesView` control.
93 |
94 | ```swift
95 | DSFDropFilesView.SwiftUI(
96 | isEnabled: true,
97 | allowsMultipleDrop: true,
98 | iconLabel: "Drop files here",
99 | selectFilesLabel: "Select Files…",
100 | selectFilesButtonIsLink: true,
101 | validateFiles: { urls in
102 | // Check the urls, and return the appropriate drop mode
103 | return .copy
104 | },
105 | dropFiles: { urls in
106 | Swift.print("\(urls)")
107 | return true
108 | },
109 | selectFiles: {
110 | Swift.print("User clicked select files")
111 | }
112 | )
113 | ```
114 |
115 | ## License
116 |
117 | ```
118 | MIT License
119 |
120 | Copyright (c) 2024 Darren Ford
121 |
122 | Permission is hereby granted, free of charge, to any person obtaining a copy
123 | of this software and associated documentation files (the "Software"), to deal
124 | in the Software without restriction, including without limitation the rights
125 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
126 | copies of the Software, and to permit persons to whom the Software is
127 | furnished to do so, subject to the following conditions:
128 |
129 | The above copyright notice and this permission notice shall be included in all
130 | copies or substantial portions of the Software.
131 |
132 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
133 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
134 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
135 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
136 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
137 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
138 | SOFTWARE.
139 | ```
140 |
--------------------------------------------------------------------------------
/Sources/DSFDropFilesView/DSFDropFilesView+SwiftUI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DSFDropFilesView+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 |
27 | #if canImport(SwiftUI) && os(macOS)
28 |
29 | import SwiftUI
30 |
31 | @available(macOS 10.15, *)
32 | public extension DSFDropFilesView {
33 | /// A SwiftUI wrapper for DSFDropFilesView
34 | struct SwiftUI {
35 | public typealias NSViewType = DSFDropFilesView
36 |
37 | /// Display properties for the view.
38 | public struct DisplayProperties {
39 | /// Is the border animated when a valid drop is available?
40 | let isAnimated: Bool
41 | /// Draw a dotted border around the outside of the view
42 | let isBordered: Bool
43 | /// The corner radius for the border
44 | let cornerRadius: CGFloat
45 | /// The border line width
46 | let lineWidth: CGFloat
47 |
48 | public init(
49 | isBordered: Bool = true,
50 | isAnimated: Bool = true,
51 | lineWidth: CGFloat = 2,
52 | cornerRadius: CGFloat = 4) {
53 | self.isBordered = isBordered
54 | self.isAnimated = isAnimated
55 | self.cornerRadius = cornerRadius
56 | self.lineWidth = lineWidth
57 | }
58 | }
59 |
60 | // MARK: Types
61 |
62 | /// User has dragged files into the view and require drop validation. Return the drag operation supported for the dragged files,
63 | /// or [] to disallow the drag.
64 | public typealias ValidateFuncType = (([URL]) -> NSDragOperation)
65 |
66 | /// Files dropped callback block. Return true if the block has successfully handled the drop, false otherwise.
67 | public typealias DropFuncType = (([URL]) -> Bool)
68 |
69 | /// Select files callback type
70 | public typealias SelectFuncType = (() -> Void)
71 |
72 | // MARK: - Properties
73 |
74 | /// The display properties for the view
75 | public let displayProperties: DisplayProperties
76 |
77 | /// Enable or disable the control
78 | public let isEnabled: Bool
79 |
80 | /// Allow dropping multiple files
81 | public let allowsMultipleDrop: Bool
82 |
83 | /// The icon for the control. If nil, no icon is displayed
84 | public let icon: NSImage?
85 |
86 | /// The label to use for the icon. If empty, label is not displayed
87 | public let iconLabel: String
88 |
89 | /// The font to use for the icon label.
90 | public let iconLabelFont: NSFont
91 |
92 | /// The label for the 'select files' button. If empty, no select files button is displayed
93 | public let selectFilesLabel: String
94 |
95 | /// If true, the select files button is displayed as a hyperlink style. If false, displays as a regular button.
96 | public let selectFilesButtonIsLink: Bool
97 |
98 | // MARK: - Callbacks
99 |
100 | /// Called when the user drags some files into the view.
101 | public let validateFiles: ValidateFuncType?
102 |
103 | /// Called when the user drops files on the view
104 | public let dropFiles: DropFuncType?
105 |
106 | /// Called when the user clicks the 'select files' button
107 | public let selectFiles: SelectFuncType?
108 |
109 | /// Initializer
110 | ///
111 | /// Needs to be provided as (by default) a default initializer is marked as internal
112 | public init(
113 | displayProperties: DisplayProperties = DisplayProperties(),
114 | isEnabled: Bool = true,
115 | allowsMultipleDrop: Bool = true,
116 | icon: NSImage? = nil,
117 | iconLabel: String = "",
118 | iconLabelFont: NSFont = .systemFont(ofSize: 16, weight: .bold),
119 | selectFilesLabel: String = "",
120 | selectFilesButtonIsLink: Bool = true,
121 | validateFiles: ValidateFuncType? = nil,
122 | dropFiles: DropFuncType? = nil,
123 | selectFiles: SelectFuncType? = nil
124 | ) {
125 | self.displayProperties = displayProperties
126 | self.isEnabled = isEnabled
127 | self.allowsMultipleDrop = allowsMultipleDrop
128 | if let i = icon {
129 | self.icon = i
130 | }
131 | else {
132 | self.icon = DSFDropFilesView.StaticImage
133 | }
134 | self.iconLabel = iconLabel
135 | self.iconLabelFont = iconLabelFont
136 | self.selectFilesLabel = selectFilesLabel
137 | self.selectFilesButtonIsLink = selectFilesButtonIsLink
138 | self.validateFiles = validateFiles
139 | self.dropFiles = dropFiles
140 | self.selectFiles = selectFiles
141 | }
142 | }
143 | }
144 |
145 | // MARK: - Coordinator
146 |
147 | /// Convenience function for optionally updating a value
148 | /// - Parameters:
149 | /// - result: The property to optionally update if 'val' is not equal to its value
150 | /// - val: The value to check against
151 | @inlinable internal func UpdateIfNotEqual(_ result: inout T, _ val: T) where T: Equatable {
152 | if result != val {
153 | result = val
154 | }
155 | }
156 |
157 | @available(macOS 10.15, *)
158 | extension DSFDropFilesView.SwiftUI: NSViewRepresentable {
159 | public func makeCoordinator() -> Coordinator {
160 | Coordinator(self)
161 | }
162 |
163 | public func makeNSView(context: Context) -> DSFDropFilesView {
164 | let view = DSFDropFilesView(frame: .zero)
165 | view.translatesAutoresizingMaskIntoConstraints = false
166 | view.dropDelegate = context.coordinator
167 | return view
168 | }
169 |
170 | public func updateNSView(_ nsView: DSFDropFilesView, context _: Context) {
171 | if self.icon !== nsView.icon {
172 | nsView.icon = self.icon
173 | nsView.showIcon = (nsView.icon != nil)
174 | }
175 |
176 | UpdateIfNotEqual(&nsView.isEnabled, self.isEnabled)
177 | UpdateIfNotEqual(&nsView.label, self.iconLabel)
178 | UpdateIfNotEqual(&nsView.labelFont, self.iconLabelFont)
179 | UpdateIfNotEqual(&nsView.selectFilesButtonLabel, self.selectFilesLabel)
180 | UpdateIfNotEqual(&nsView.allowsMultipleDrop, self.allowsMultipleDrop)
181 | UpdateIfNotEqual(&nsView.selectFilesButtonIsLink, self.selectFilesButtonIsLink)
182 |
183 | UpdateIfNotEqual(&nsView.isBordered, self.displayProperties.isBordered)
184 | UpdateIfNotEqual(&nsView.isAnimated, self.displayProperties.isAnimated)
185 | UpdateIfNotEqual(&nsView.lineWidth, self.displayProperties.lineWidth)
186 | UpdateIfNotEqual(&nsView.cornerRadius, self.displayProperties.cornerRadius)
187 | }
188 | }
189 |
190 | // MARK: - Delegate handling
191 |
192 | @available(macOS 10.15, *)
193 | public extension DSFDropFilesView.SwiftUI {
194 | class Coordinator: NSObject, DSFDropFilesViewProtocol {
195 | let parent: DSFDropFilesView.SwiftUI
196 | init(_ dropView: DSFDropFilesView.SwiftUI) {
197 | self.parent = dropView
198 | }
199 |
200 | public func dropFilesView(_: DSFDropFilesView, validateFiles: [URL]) -> NSDragOperation {
201 | return self.parent.validateFiles?(validateFiles) ?? []
202 | }
203 |
204 | public func dropFilesView(_: DSFDropFilesView, didDropFiles files: [URL]) -> Bool {
205 | return self.parent.dropFiles?(files) ?? false
206 | }
207 |
208 | public func dropFilesViewWantsSelectFiles(_: DSFDropFilesView) {
209 | self.parent.selectFiles?()
210 | }
211 | }
212 | }
213 |
214 | #endif
215 |
--------------------------------------------------------------------------------
/Sources/DSFDropFilesView/private/DSFDropFilesView+image.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DSFDropFilesView+image.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 of this software and associated
9 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
11 | // permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all copies or substantial
14 | // portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
18 | // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | //
21 |
22 | #if os(macOS)
23 |
24 | import AppKit
25 |
26 | internal extension DSFDropFilesView {
27 |
28 | static let StaticImage: NSImage = {
29 | let im = NSImage.init(size: NSSize(width: 64, height: 64), flipped: false) { rect in
30 | let bezier = DSFDropFilesView.iconBezier()
31 | NSColor.black.set()
32 | bezier.fill()
33 | return true
34 | }
35 | im.isTemplate = true
36 | return im
37 | }()
38 |
39 | static func iconBezier() -> NSBezierPath {
40 | //// Text Drawing
41 | let textPath = NSBezierPath()
42 | textPath.move(to: NSPoint(x: 33.2, y: 32.68))
43 | textPath.curve(to: NSPoint(x: 32.89, y: 33.4), controlPoint1: NSPoint(x: 33.2, y: 32.95), controlPoint2: NSPoint(x: 33.09, y: 33.19))
44 | textPath.curve(to: NSPoint(x: 32.14, y: 33.71), controlPoint1: NSPoint(x: 32.68, y: 33.6), controlPoint2: NSPoint(x: 32.44, y: 33.71))
45 | textPath.curve(to: NSPoint(x: 31.4, y: 33.4), controlPoint1: NSPoint(x: 31.85, y: 33.71), controlPoint2: NSPoint(x: 31.6, y: 33.6))
46 | textPath.curve(to: NSPoint(x: 31.09, y: 32.68), controlPoint1: NSPoint(x: 31.19, y: 33.19), controlPoint2: NSPoint(x: 31.09, y: 32.95))
47 | textPath.line(to: NSPoint(x: 31.09, y: 17.86))
48 | textPath.line(to: NSPoint(x: 31.18, y: 11.76))
49 | textPath.line(to: NSPoint(x: 25.61, y: 17.42))
50 | textPath.line(to: NSPoint(x: 23.18, y: 19.91))
51 | textPath.curve(to: NSPoint(x: 22.86, y: 20.11), controlPoint1: NSPoint(x: 23.1, y: 19.98), controlPoint2: NSPoint(x: 22.99, y: 20.05))
52 | textPath.curve(to: NSPoint(x: 22.45, y: 20.2), controlPoint1: NSPoint(x: 22.72, y: 20.17), controlPoint2: NSPoint(x: 22.58, y: 20.2))
53 | textPath.curve(to: NSPoint(x: 21.71, y: 19.91), controlPoint1: NSPoint(x: 22.15, y: 20.2), controlPoint2: NSPoint(x: 21.91, y: 20.1))
54 | textPath.curve(to: NSPoint(x: 21.42, y: 19.17), controlPoint1: NSPoint(x: 21.52, y: 19.71), controlPoint2: NSPoint(x: 21.42, y: 19.47))
55 | textPath.curve(to: NSPoint(x: 21.61, y: 18.56), controlPoint1: NSPoint(x: 21.42, y: 18.9), controlPoint2: NSPoint(x: 21.48, y: 18.7))
56 | textPath.curve(to: NSPoint(x: 22.01, y: 18.12), controlPoint1: NSPoint(x: 21.74, y: 18.42), controlPoint2: NSPoint(x: 21.87, y: 18.28))
57 | textPath.line(to: NSPoint(x: 31.35, y: 8.98))
58 | textPath.curve(to: NSPoint(x: 31.73, y: 8.69), controlPoint1: NSPoint(x: 31.47, y: 8.84), controlPoint2: NSPoint(x: 31.6, y: 8.74))
59 | textPath.curve(to: NSPoint(x: 32.14, y: 8.6), controlPoint1: NSPoint(x: 31.87, y: 8.63), controlPoint2: NSPoint(x: 32.01, y: 8.6))
60 | textPath.curve(to: NSPoint(x: 32.54, y: 8.69), controlPoint1: NSPoint(x: 32.28, y: 8.6), controlPoint2: NSPoint(x: 32.41, y: 8.63))
61 | textPath.curve(to: NSPoint(x: 32.93, y: 8.98), controlPoint1: NSPoint(x: 32.67, y: 8.74), controlPoint2: NSPoint(x: 32.8, y: 8.84))
62 | textPath.line(to: NSPoint(x: 42.25, y: 18.12))
63 | textPath.curve(to: NSPoint(x: 42.67, y: 18.56), controlPoint1: NSPoint(x: 42.41, y: 18.28), controlPoint2: NSPoint(x: 42.55, y: 18.42))
64 | textPath.curve(to: NSPoint(x: 42.87, y: 19.17), controlPoint1: NSPoint(x: 42.8, y: 18.7), controlPoint2: NSPoint(x: 42.87, y: 18.9))
65 | textPath.curve(to: NSPoint(x: 42.56, y: 19.91), controlPoint1: NSPoint(x: 42.87, y: 19.47), controlPoint2: NSPoint(x: 42.76, y: 19.71))
66 | textPath.curve(to: NSPoint(x: 41.84, y: 20.2), controlPoint1: NSPoint(x: 42.35, y: 20.1), controlPoint2: NSPoint(x: 42.11, y: 20.2))
67 | textPath.curve(to: NSPoint(x: 41.42, y: 20.11), controlPoint1: NSPoint(x: 41.7, y: 20.2), controlPoint2: NSPoint(x: 41.56, y: 20.17))
68 | textPath.curve(to: NSPoint(x: 41.08, y: 19.91), controlPoint1: NSPoint(x: 41.27, y: 20.05), controlPoint2: NSPoint(x: 41.16, y: 19.98))
69 | textPath.line(to: NSPoint(x: 38.65, y: 17.42))
70 | textPath.line(to: NSPoint(x: 33.11, y: 11.76))
71 | textPath.line(to: NSPoint(x: 33.2, y: 17.86))
72 | textPath.line(to: NSPoint(x: 33.2, y: 32.68))
73 | textPath.close()
74 | textPath.move(to: NSPoint(x: 16.35, y: 1.13))
75 | textPath.line(to: NSPoint(x: 47.93, y: 1.13))
76 | textPath.curve(to: NSPoint(x: 53.79, y: 3.13), controlPoint1: NSPoint(x: 50.51, y: 1.13), controlPoint2: NSPoint(x: 52.46, y: 1.8))
77 | textPath.curve(to: NSPoint(x: 55.79, y: 9.04), controlPoint1: NSPoint(x: 55.12, y: 4.47), controlPoint2: NSPoint(x: 55.79, y: 6.44))
78 | textPath.line(to: NSPoint(x: 55.79, y: 37.69))
79 | textPath.curve(to: NSPoint(x: 55.64, y: 39.68), controlPoint1: NSPoint(x: 55.79, y: 38.47), controlPoint2: NSPoint(x: 55.74, y: 39.13))
80 | textPath.curve(to: NSPoint(x: 55.1, y: 41.18), controlPoint1: NSPoint(x: 55.54, y: 40.23), controlPoint2: NSPoint(x: 55.36, y: 40.73))
81 | textPath.curve(to: NSPoint(x: 53.97, y: 42.55), controlPoint1: NSPoint(x: 54.83, y: 41.63), controlPoint2: NSPoint(x: 54.46, y: 42.08))
82 | textPath.line(to: NSPoint(x: 35.37, y: 61.24))
83 | textPath.curve(to: NSPoint(x: 33.99, y: 62.34), controlPoint1: NSPoint(x: 34.9, y: 61.71), controlPoint2: NSPoint(x: 34.44, y: 62.08))
84 | textPath.curve(to: NSPoint(x: 32.51, y: 62.9), controlPoint1: NSPoint(x: 33.54, y: 62.61), controlPoint2: NSPoint(x: 33.05, y: 62.79))
85 | textPath.curve(to: NSPoint(x: 30.59, y: 63.06), controlPoint1: NSPoint(x: 31.97, y: 63.01), controlPoint2: NSPoint(x: 31.33, y: 63.06))
86 | textPath.line(to: NSPoint(x: 16.35, y: 63.06))
87 | textPath.curve(to: NSPoint(x: 10.49, y: 61.05), controlPoint1: NSPoint(x: 13.79, y: 63.06), controlPoint2: NSPoint(x: 11.84, y: 62.39))
88 | textPath.curve(to: NSPoint(x: 8.47, y: 55.18), controlPoint1: NSPoint(x: 9.14, y: 59.72), controlPoint2: NSPoint(x: 8.47, y: 57.76))
89 | textPath.line(to: NSPoint(x: 8.47, y: 9.04))
90 | textPath.curve(to: NSPoint(x: 10.46, y: 3.12), controlPoint1: NSPoint(x: 8.47, y: 6.42), controlPoint2: NSPoint(x: 9.13, y: 4.45))
91 | textPath.curve(to: NSPoint(x: 16.35, y: 1.13), controlPoint1: NSPoint(x: 11.79, y: 1.79), controlPoint2: NSPoint(x: 13.75, y: 1.13))
92 | textPath.close()
93 | textPath.move(to: NSPoint(x: 16.44, y: 3.29))
94 | textPath.curve(to: NSPoint(x: 12.1, y: 4.76), controlPoint1: NSPoint(x: 14.53, y: 3.29), controlPoint2: NSPoint(x: 13.08, y: 3.78))
95 | textPath.curve(to: NSPoint(x: 10.64, y: 9.1), controlPoint1: NSPoint(x: 11.13, y: 5.74), controlPoint2: NSPoint(x: 10.64, y: 7.18))
96 | textPath.line(to: NSPoint(x: 10.64, y: 55.09))
97 | textPath.curve(to: NSPoint(x: 12.1, y: 59.38), controlPoint1: NSPoint(x: 10.64, y: 56.95), controlPoint2: NSPoint(x: 11.13, y: 58.38))
98 | textPath.curve(to: NSPoint(x: 16.44, y: 60.89), controlPoint1: NSPoint(x: 13.08, y: 60.39), controlPoint2: NSPoint(x: 14.53, y: 60.89))
99 | textPath.line(to: NSPoint(x: 31.09, y: 60.89))
100 | textPath.line(to: NSPoint(x: 31.09, y: 42.44))
101 | textPath.curve(to: NSPoint(x: 32.14, y: 39.27), controlPoint1: NSPoint(x: 31.09, y: 40.99), controlPoint2: NSPoint(x: 31.44, y: 39.94))
102 | textPath.curve(to: NSPoint(x: 35.22, y: 38.28), controlPoint1: NSPoint(x: 32.85, y: 38.61), controlPoint2: NSPoint(x: 33.87, y: 38.28))
103 | textPath.line(to: NSPoint(x: 53.62, y: 38.28))
104 | textPath.line(to: NSPoint(x: 53.62, y: 9.1))
105 | textPath.curve(to: NSPoint(x: 52.15, y: 4.76), controlPoint1: NSPoint(x: 53.62, y: 7.18), controlPoint2: NSPoint(x: 53.13, y: 5.74))
106 | textPath.curve(to: NSPoint(x: 47.82, y: 3.29), controlPoint1: NSPoint(x: 51.18, y: 3.78), controlPoint2: NSPoint(x: 49.73, y: 3.29))
107 | textPath.line(to: NSPoint(x: 16.44, y: 3.29))
108 | textPath.close()
109 | textPath.move(to: NSPoint(x: 35.39, y: 40.38))
110 | textPath.curve(to: NSPoint(x: 33.68, y: 40.88), controlPoint1: NSPoint(x: 34.59, y: 40.38), controlPoint2: NSPoint(x: 34.02, y: 40.55))
111 | textPath.curve(to: NSPoint(x: 33.17, y: 42.61), controlPoint1: NSPoint(x: 33.34, y: 41.21), controlPoint2: NSPoint(x: 33.17, y: 41.79))
112 | textPath.line(to: NSPoint(x: 33.17, y: 60.39))
113 | textPath.line(to: NSPoint(x: 53.12, y: 40.38))
114 | textPath.line(to: NSPoint(x: 35.39, y: 40.38))
115 | textPath.close()
116 | return textPath
117 | }
118 | }
119 |
120 | #endif
121 |
--------------------------------------------------------------------------------
/Sources/DSFDropFilesView/DSFDropFilesView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DSFDropFilesView.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 of this software and associated
9 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
11 | // permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all copies or substantial
14 | // portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
18 | // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | //
21 |
22 | #if os(macOS)
23 |
24 | import AppKit
25 | import DSFAppearanceManager
26 |
27 | /// A view that support dropping files
28 | @IBDesignable
29 | @objc public class DSFDropFilesView: NSView {
30 | // MARK: - Public accessors
31 |
32 | /// Enable or disable the control
33 | @IBInspectable public dynamic var isEnabled: Bool = true {
34 | didSet {
35 | self.selectButton.isEnabled = self.isEnabled
36 |
37 | // Force the cursor to update to reflect the new state
38 | self.window?.invalidateCursorRects(for: self.selectButton)
39 | }
40 | }
41 |
42 | /// The drop delegate.
43 | @IBOutlet public var dropDelegate: DSFDropFilesViewProtocol? {
44 | didSet {
45 | self.syncTitle()
46 | }
47 | }
48 |
49 | /// Do we support dropping multiple files at once?
50 | @IBInspectable public var allowsMultipleDrop: Bool = true {
51 | didSet {
52 | self.syncTitle()
53 | }
54 | }
55 |
56 | /// The label for the 'select files...' button. If empty, the button is not displayed.
57 | @IBInspectable public var selectFilesButtonLabel: String = "" {
58 | didSet {
59 | self.syncTitle()
60 | }
61 | }
62 |
63 | /// If the select files button is shown, do we display it as a hyperlink or a regular button?
64 | @IBInspectable public var selectFilesButtonIsLink: Bool = true {
65 | didSet {
66 | self.syncTitle()
67 | }
68 | }
69 |
70 | /// Should the drop target display an icon.
71 | @IBInspectable public var showIcon: Bool = true {
72 | didSet {
73 | self.syncTitle()
74 | }
75 | }
76 |
77 | /// The image to be displayed.
78 | @IBInspectable public var icon: NSImage? = DSFDropFilesView.StaticImage {
79 | didSet {
80 | self.imageView.image = self.icon
81 | self.syncImage()
82 | }
83 | }
84 |
85 | /// Display a informational label. If empty, the label is hidden.
86 | @IBInspectable public var label: String = "" {
87 | didSet {
88 | self.syncTitle()
89 | }
90 | }
91 |
92 | /// The font to use for the label
93 | @IBInspectable public var labelFont: NSFont? {
94 | get { self.imageLabel.font }
95 | set { self.imageLabel.font = newValue }
96 | }
97 |
98 | /// Display a border around the drop target
99 | @IBInspectable public var isBordered: Bool = true {
100 | didSet {
101 | self.outerBoundary.isHidden = !self.isBordered
102 | self.syncTitle()
103 | }
104 | }
105 |
106 | /// Width of the border line around the control
107 | @IBInspectable public var lineWidth: CGFloat = 2 {
108 | didSet {
109 | self.outerBoundary.lineWidth = self.lineWidth
110 | }
111 | }
112 |
113 | /// The corner radius for the border
114 | @IBInspectable public var cornerRadius: CGFloat = 4
115 | var borderInset: CGFloat {
116 | return self.cornerRadius / 2.0
117 | }
118 |
119 | /// Should we animate the border when we can drop?
120 | @IBInspectable public var isAnimated: Bool = true
121 |
122 | // MARK: - Initialization
123 |
124 | /// Create a drop view
125 | override public init(frame frameRect: NSRect) {
126 | super.init(frame: frameRect)
127 | self.configureControl()
128 | }
129 |
130 | /// Create a drop view
131 | required public init?(coder: NSCoder) {
132 | super.init(coder: coder)
133 | self.configureControl()
134 | }
135 |
136 | deinit {
137 | DSFAppearanceCache.shared.deregister(self)
138 | }
139 |
140 | // MARK: - Private definitions
141 |
142 | private let outerBoundary = CAShapeLayer()
143 |
144 | private lazy var containerStack: NSStackView = {
145 | let s = NSStackView()
146 | s.translatesAutoresizingMaskIntoConstraints = false
147 | s.wantsLayer = true
148 | s.edgeInsets = NSEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
149 |
150 | s.orientation = .vertical
151 | s.spacing = 4
152 | s.alignment = .centerX
153 | s.setContentHuggingPriority(NSLayoutConstraint.Priority(50), for: .horizontal)
154 | s.setContentHuggingPriority(NSLayoutConstraint.Priority(50), for: .vertical)
155 |
156 | s.setHuggingPriority(NSLayoutConstraint.Priority(50), for: .horizontal)
157 | s.setHuggingPriority(NSLayoutConstraint.Priority(50), for: .vertical)
158 |
159 | s.setContentCompressionResistancePriority(.required, for: .horizontal)
160 | s.setContentCompressionResistancePriority(.required, for: .vertical)
161 |
162 | s.addArrangedSubview(self.imageView)
163 | s.addArrangedSubview(self.imageLabel)
164 | s.addArrangedSubview(self.buttonSpacerView)
165 | s.addArrangedSubview(self.selectButton)
166 |
167 | return s
168 | }()
169 |
170 | private lazy var buttonSpacerView: NSView = {
171 | let sep = NSView(frame: .zero)
172 | sep.translatesAutoresizingMaskIntoConstraints = false
173 | sep.addConstraint(NSLayoutConstraint(item: sep, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 4))
174 | sep.addConstraint(NSLayoutConstraint(item: sep, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 8))
175 | return sep
176 | }()
177 |
178 | private lazy var imageView: NSImageView = {
179 | let i = NSImageView()
180 | i.translatesAutoresizingMaskIntoConstraints = false
181 | i.wantsLayer = true
182 | i.imageScaling = .scaleProportionallyDown
183 | i.image = self.icon
184 |
185 | i.isEditable = false
186 | i.unregisterDraggedTypes()
187 |
188 | i.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
189 | i.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
190 |
191 | return i
192 | }()
193 |
194 | private lazy var imageLabel: NSTextField = {
195 | let t = NSTextField()
196 | t.translatesAutoresizingMaskIntoConstraints = false
197 | t.wantsLayer = true
198 | t.drawsBackground = false
199 | t.isBezeled = false
200 | t.font = NSFont.systemFont(ofSize: 16, weight: .bold)
201 | t.textColor = NSColor.tertiaryLabelColor
202 | t.stringValue = self.label
203 | t.alignment = .center
204 | t.isEditable = false
205 | t.isSelectable = false
206 | t.lineBreakMode = .byWordWrapping
207 |
208 | t.setContentHuggingPriority(.defaultLow, for: .horizontal)
209 | t.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
210 |
211 | return t
212 | }()
213 |
214 | private lazy var selectButton: DSFHandButton = {
215 | let button = DSFHandButton()
216 | button.wantsLayer = true
217 | button.translatesAutoresizingMaskIntoConstraints = false
218 | button.target = self
219 | button.action = #selector(self.selectFiles(_:))
220 | button.bezelStyle = .rounded
221 | button.isBordered = false
222 | button.controlSize = .regular
223 | button.isEnabled = true
224 | return button
225 | }()
226 | }
227 |
228 | // MARK: - Action callbacks
229 |
230 | extension DSFDropFilesView {
231 | @objc func selectFiles(_ sender: Any) {
232 | if let userSelectedFiles = self.dropDelegate?.dropFilesViewWantsSelectFiles {
233 | userSelectedFiles(self)
234 | }
235 | }
236 | }
237 |
238 | // MARK: - UI Layout
239 |
240 | extension DSFDropFilesView {
241 | override public func prepareForInterfaceBuilder() {
242 | self.configureControl()
243 | }
244 |
245 | override public func layout() {
246 | super.layout()
247 | self.outerBoundary.path = CGPath(
248 | roundedRect: self.bounds.insetBy(dx: self.borderInset, dy: self.borderInset),
249 | cornerWidth: self.cornerRadius,
250 | cornerHeight: self.cornerRadius, transform: nil
251 | )
252 | }
253 |
254 | private func syncImage() {
255 | let template = self.icon?.isTemplate ?? false
256 | self.imageView.alphaValue = template ? 0.4 : 1.0
257 | }
258 |
259 | private func syncTitle() {
260 | let buttonTitle = self.selectFilesButtonLabel
261 | if self.selectFilesButtonIsLink {
262 | let att = NSAttributedString(
263 | string: buttonTitle,
264 | attributes: [
265 | NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue,
266 | NSAttributedString.Key.foregroundColor: NSColor.linkColor,
267 | ]
268 | )
269 | self.selectButton.isBordered = false
270 | self.selectButton.attributedTitle = att
271 | self.selectButton.wantsHandCursor = true
272 | }
273 | else {
274 | self.selectButton.isBordered = true
275 | self.selectButton.title = buttonTitle
276 | self.selectButton.wantsHandCursor = false
277 | }
278 |
279 | self.imageView.image = self.icon
280 | self.syncImage()
281 | self.imageView.isHidden = !self.showIcon
282 |
283 | self.imageLabel.stringValue = self.label
284 |
285 | // Hide the label if the label text is empty
286 | self.imageLabel.isHidden = self.label.isEmpty
287 |
288 | // Hide the select files button if no text has been set
289 | let hideSelectFilesButton = self.selectFilesButtonLabel.isEmpty
290 | self.buttonSpacerView.isHidden = hideSelectFilesButton
291 | self.selectButton.isHidden = hideSelectFilesButton
292 | }
293 |
294 | private func configureControl() {
295 | self.wantsLayer = true
296 |
297 | if #available(OSX 10.13, *) {
298 | self.registerForDraggedTypes([.fileURL])
299 | }
300 | else {
301 | let furl = NSPasteboard.PasteboardType(kUTTypeFileURL as String)
302 | self.registerForDraggedTypes([furl])
303 | }
304 |
305 | self.outerBoundary.fillColor = self.backgroundColor()
306 | self.outerBoundary.strokeColor = self.backgroundStrokeColor()
307 | self.outerBoundary.lineWidth = self.lineWidth
308 |
309 | self.outerBoundary.lineDashPattern = [4, 4]
310 | self.outerBoundary.lineDashPhase = 0
311 |
312 | self.layer!.addSublayer(self.outerBoundary)
313 |
314 | let stack = self.containerStack
315 | self.addSubview(stack)
316 |
317 | self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-(8)-[but]-(8)-|", options: .alignAllCenterY, metrics: nil, views: ["but": stack]))
318 | self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(>=8)-[but]-(>=8)-|", options: .alignAllCenterX, metrics: nil, views: ["but": stack]))
319 |
320 | self.addConstraint(NSLayoutConstraint(item: stack, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
321 | self.addConstraint(NSLayoutConstraint(item: stack, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
322 |
323 | // Register for appearance updates
324 | DSFAppearanceCache.shared.register(self)
325 |
326 | self.syncTitle()
327 | }
328 | }
329 |
330 | extension DSFDropFilesView: DSFAppearanceCacheNotifiable {
331 | public func appearanceDidChange() {
332 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
333 | self.outerBoundary.strokeColor = self.backgroundStrokeColor()
334 | }
335 | }
336 | }
337 |
338 | // MARK: - Drag and drop
339 |
340 | public extension DSFDropFilesView {
341 | private func filesOnPasteboard(for sender: NSDraggingInfo) -> [URL]? {
342 | let pb = sender.draggingPasteboard
343 | guard let objs = pb.readObjects(forClasses: [NSURL.self], options: nil) as? [NSURL] else {
344 | return nil
345 | }
346 |
347 | let urls = objs.compactMap { $0 as URL }
348 | return urls.count == 0 ? nil : urls
349 | }
350 |
351 | // Called when the user has dropped a file
352 |
353 | override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool {
354 | if self.isEnabled,
355 | let delegate = self.dropDelegate,
356 | let files = self.filesOnPasteboard(for: sender)
357 | {
358 | return delegate.dropFilesView(self, validateFiles: files) != []
359 | }
360 | return false
361 | }
362 |
363 | // When the user has dragged INTO the view
364 |
365 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
366 | var dragOperation: NSDragOperation = []
367 |
368 | // If there are no files on the pasteboard then do nothing
369 | guard let files = self.filesOnPasteboard(for: sender) else {
370 | return []
371 | }
372 |
373 | // If we are in single select and there are multiple files then don't allow drop
374 | if !self.allowsMultipleDrop, files.count != 1 {
375 | return []
376 | }
377 |
378 | if let delegate = self.dropDelegate {
379 | dragOperation = delegate.dropFilesView(self, validateFiles: files)
380 | }
381 |
382 | if dragOperation == [] {
383 | self.outerBoundary.strokeColor = self.backgroundStrokeColor()
384 | self.outerBoundary.fillColor = self.backgroundColor()
385 | }
386 | else {
387 | self.outerBoundary.strokeColor = self.backgroundActiveStrokeColor()
388 | self.outerBoundary.fillColor = self.backgroundActiveColor()
389 |
390 | self.startAnimation()
391 | }
392 |
393 | return dragOperation
394 | }
395 |
396 | // When the user has dragged OUT of the view or drag is cancelled
397 |
398 | override func draggingExited(_: NSDraggingInfo?) {
399 | self.outerBoundary.strokeColor = self.backgroundStrokeColor()
400 | self.outerBoundary.fillColor = self.backgroundColor()
401 | self.stopAnimation()
402 | }
403 |
404 | // Perform the drag operation
405 |
406 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
407 | self.stopAnimation()
408 |
409 | self.outerBoundary.strokeColor = self.backgroundStrokeColor()
410 | self.outerBoundary.fillColor = self.backgroundColor()
411 |
412 | if let delegate = self.dropDelegate,
413 | let files = self.filesOnPasteboard(for: sender)
414 | {
415 | return delegate.dropFilesView(self, didDropFiles: files)
416 | }
417 |
418 | return false
419 | }
420 | }
421 |
422 | // MARK: - Colors
423 |
424 | extension DSFDropFilesView {
425 | func backgroundColor() -> CGColor {
426 | return NSColor(calibratedWhite: 0, alpha: 0.05).cgColor
427 | }
428 |
429 | func backgroundStrokeColor() -> CGColor {
430 | var color: CGColor!
431 | self.usingEffectiveAppearance {
432 | color = NSColor.tertiaryLabelColor.cgColor
433 | }
434 | return color
435 | }
436 |
437 | func backgroundActiveColor() -> CGColor {
438 | var color: CGColor!
439 | self.usingEffectiveAppearance {
440 | color = NSColor.quaternaryLabelColor.cgColor
441 | }
442 | return color
443 | }
444 |
445 | func backgroundActiveStrokeColor() -> CGColor {
446 | var color: CGColor!
447 | self.usingEffectiveAppearance {
448 | color = NSColor.secondaryLabelColor.cgColor
449 | }
450 | return color
451 | }
452 | }
453 |
454 | // MARK: - Animation
455 |
456 | extension DSFDropFilesView {
457 | func startAnimation() {
458 | if !self.isAnimated || DSFAppearanceManager.ReduceMotion {
459 | return
460 | }
461 |
462 | let lineDashPhaseAnimation = CABasicAnimation(keyPath: "lineDashPhase")
463 | lineDashPhaseAnimation.byValue = 8.0
464 | lineDashPhaseAnimation.duration = 0.75
465 | lineDashPhaseAnimation.timingFunction = CAMediaTimingFunction(name: .linear)
466 | lineDashPhaseAnimation.repeatCount = .greatestFiniteMagnitude
467 |
468 | self.outerBoundary.add(lineDashPhaseAnimation, forKey: "lineDashPhaseAnimation")
469 | }
470 |
471 | func stopAnimation() {
472 | self.outerBoundary.removeAllAnimations()
473 | }
474 | }
475 |
476 | #endif
477 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXAggregateTarget section */
10 | 23D29C41256C890300E5CBC0 /* All */ = {
11 | isa = PBXAggregateTarget;
12 | buildConfigurationList = 23D29C44256C890300E5CBC0 /* Build configuration list for PBXAggregateTarget "All" */;
13 | buildPhases = (
14 | );
15 | dependencies = (
16 | 230A4B8E256CA760009B4689 /* PBXTargetDependency */,
17 | 23D29C49256C890800E5CBC0 /* PBXTargetDependency */,
18 | 23D29C4B256C890800E5CBC0 /* PBXTargetDependency */,
19 | );
20 | name = All;
21 | productName = All;
22 | };
23 | /* End PBXAggregateTarget section */
24 |
25 | /* Begin PBXBuildFile section */
26 | 230A4B79256CA750009B4689 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 230A4B78256CA750009B4689 /* AppDelegate.m */; };
27 | 230A4B7C256CA750009B4689 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 230A4B7B256CA750009B4689 /* ViewController.m */; };
28 | 230A4B7E256CA751009B4689 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 230A4B7D256CA751009B4689 /* Assets.xcassets */; };
29 | 230A4B81256CA751009B4689 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 230A4B7F256CA751009B4689 /* Main.storyboard */; };
30 | 230A4B84256CA751009B4689 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 230A4B83256CA751009B4689 /* main.m */; };
31 | 235981A8256B333F000D2479 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 235981A7256B333F000D2479 /* AppDelegate.swift */; };
32 | 235981AA256B333F000D2479 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 235981A9256B333F000D2479 /* ContentView.swift */; };
33 | 235981AC256B3340000D2479 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 235981AB256B3340000D2479 /* Assets.xcassets */; };
34 | 235981AF256B3340000D2479 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 235981AE256B3340000D2479 /* Preview Assets.xcassets */; };
35 | 235981B2256B3340000D2479 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 235981B0256B3340000D2479 /* Main.storyboard */; };
36 | 23D18D0A286FA4B500D5A0D2 /* DSFDropFilesView in Frameworks */ = {isa = PBXBuildFile; productRef = 23D18D09286FA4B500D5A0D2 /* DSFDropFilesView */; };
37 | 23D18D0C286FA4C000D5A0D2 /* DSFDropFilesView in Frameworks */ = {isa = PBXBuildFile; productRef = 23D18D0B286FA4C000D5A0D2 /* DSFDropFilesView */; };
38 | 23D18D0E286FA4DF00D5A0D2 /* DSFDropFilesView in Frameworks */ = {isa = PBXBuildFile; productRef = 23D18D0D286FA4DF00D5A0D2 /* DSFDropFilesView */; };
39 | 23E5973C2547D4460080BE0E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E5973B2547D4460080BE0E /* AppDelegate.swift */; };
40 | 23E5973E2547D4460080BE0E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E5973D2547D4460080BE0E /* ViewController.swift */; };
41 | 23E597402547D4470080BE0E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23E5973F2547D4470080BE0E /* Assets.xcassets */; };
42 | 23E597432547D4470080BE0E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23E597412547D4470080BE0E /* Main.storyboard */; };
43 | /* End PBXBuildFile section */
44 |
45 | /* Begin PBXContainerItemProxy section */
46 | 230A4B8D256CA760009B4689 /* PBXContainerItemProxy */ = {
47 | isa = PBXContainerItemProxy;
48 | containerPortal = 23E597302547D4460080BE0E /* Project object */;
49 | proxyType = 1;
50 | remoteGlobalIDString = 230A4B74256CA750009B4689;
51 | remoteInfo = "DSFDropFilesView ObjC Demo";
52 | };
53 | 23D29C48256C890800E5CBC0 /* PBXContainerItemProxy */ = {
54 | isa = PBXContainerItemProxy;
55 | containerPortal = 23E597302547D4460080BE0E /* Project object */;
56 | proxyType = 1;
57 | remoteGlobalIDString = 23E597372547D4460080BE0E;
58 | remoteInfo = "DSFDropFilesView Demo";
59 | };
60 | 23D29C4A256C890800E5CBC0 /* PBXContainerItemProxy */ = {
61 | isa = PBXContainerItemProxy;
62 | containerPortal = 23E597302547D4460080BE0E /* Project object */;
63 | proxyType = 1;
64 | remoteGlobalIDString = 235981A4256B333F000D2479;
65 | remoteInfo = "DSFDropFilesView SwiftUI Demo";
66 | };
67 | /* End PBXContainerItemProxy section */
68 |
69 | /* Begin PBXFileReference section */
70 | 230A4B75256CA750009B4689 /* DSFDropFilesView ObjC Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFDropFilesView ObjC Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
71 | 230A4B77256CA750009B4689 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
72 | 230A4B78256CA750009B4689 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
73 | 230A4B7A256CA750009B4689 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
74 | 230A4B7B256CA750009B4689 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
75 | 230A4B7D256CA751009B4689 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
76 | 230A4B80256CA751009B4689 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
77 | 230A4B82256CA751009B4689 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
78 | 230A4B83256CA751009B4689 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
79 | 230A4B85256CA751009B4689 /* DSFDropFilesView_ObjC_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFDropFilesView_ObjC_Demo.entitlements; sourceTree = ""; };
80 | 235981A5256B333F000D2479 /* DSFDropFilesView SwiftUI Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFDropFilesView SwiftUI Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
81 | 235981A7256B333F000D2479 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
82 | 235981A9256B333F000D2479 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
83 | 235981AB256B3340000D2479 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
84 | 235981AE256B3340000D2479 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
85 | 235981B1256B3340000D2479 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
86 | 235981B3256B3340000D2479 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
87 | 235981B4256B3340000D2479 /* DSFDropFilesView_SwiftUI_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFDropFilesView_SwiftUI_Demo.entitlements; sourceTree = ""; };
88 | 23D29C4F256C8DDC00E5CBC0 /* DSFDropFilesView */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DSFDropFilesView; path = ../..; sourceTree = ""; };
89 | 23E597382547D4460080BE0E /* DSFDropFilesView Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFDropFilesView Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
90 | 23E5973B2547D4460080BE0E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
91 | 23E5973D2547D4460080BE0E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
92 | 23E5973F2547D4470080BE0E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
93 | 23E597422547D4470080BE0E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
94 | 23E597442547D4470080BE0E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
95 | 23E597452547D4470080BE0E /* DSFDropFilesView_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFDropFilesView_Demo.entitlements; sourceTree = ""; };
96 | /* End PBXFileReference section */
97 |
98 | /* Begin PBXFrameworksBuildPhase section */
99 | 230A4B72256CA750009B4689 /* Frameworks */ = {
100 | isa = PBXFrameworksBuildPhase;
101 | buildActionMask = 2147483647;
102 | files = (
103 | 23D18D0E286FA4DF00D5A0D2 /* DSFDropFilesView in Frameworks */,
104 | );
105 | runOnlyForDeploymentPostprocessing = 0;
106 | };
107 | 235981A2256B333F000D2479 /* Frameworks */ = {
108 | isa = PBXFrameworksBuildPhase;
109 | buildActionMask = 2147483647;
110 | files = (
111 | 23D18D0C286FA4C000D5A0D2 /* DSFDropFilesView in Frameworks */,
112 | );
113 | runOnlyForDeploymentPostprocessing = 0;
114 | };
115 | 23E597352547D4460080BE0E /* Frameworks */ = {
116 | isa = PBXFrameworksBuildPhase;
117 | buildActionMask = 2147483647;
118 | files = (
119 | 23D18D0A286FA4B500D5A0D2 /* DSFDropFilesView in Frameworks */,
120 | );
121 | runOnlyForDeploymentPostprocessing = 0;
122 | };
123 | /* End PBXFrameworksBuildPhase section */
124 |
125 | /* Begin PBXGroup section */
126 | 230A4B76256CA750009B4689 /* DSFDropFilesView ObjC Demo */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 230A4B77256CA750009B4689 /* AppDelegate.h */,
130 | 230A4B78256CA750009B4689 /* AppDelegate.m */,
131 | 230A4B7A256CA750009B4689 /* ViewController.h */,
132 | 230A4B7B256CA750009B4689 /* ViewController.m */,
133 | 230A4B7D256CA751009B4689 /* Assets.xcassets */,
134 | 230A4B7F256CA751009B4689 /* Main.storyboard */,
135 | 230A4B82256CA751009B4689 /* Info.plist */,
136 | 230A4B83256CA751009B4689 /* main.m */,
137 | 230A4B85256CA751009B4689 /* DSFDropFilesView_ObjC_Demo.entitlements */,
138 | );
139 | path = "DSFDropFilesView ObjC Demo";
140 | sourceTree = "";
141 | };
142 | 235981A6256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */ = {
143 | isa = PBXGroup;
144 | children = (
145 | 235981A7256B333F000D2479 /* AppDelegate.swift */,
146 | 235981A9256B333F000D2479 /* ContentView.swift */,
147 | 235981AB256B3340000D2479 /* Assets.xcassets */,
148 | 235981B0256B3340000D2479 /* Main.storyboard */,
149 | 235981B3256B3340000D2479 /* Info.plist */,
150 | 235981B4256B3340000D2479 /* DSFDropFilesView_SwiftUI_Demo.entitlements */,
151 | 235981AD256B3340000D2479 /* Preview Content */,
152 | );
153 | path = "DSFDropFilesView SwiftUI Demo";
154 | sourceTree = "";
155 | };
156 | 235981AD256B3340000D2479 /* Preview Content */ = {
157 | isa = PBXGroup;
158 | children = (
159 | 235981AE256B3340000D2479 /* Preview Assets.xcassets */,
160 | );
161 | path = "Preview Content";
162 | sourceTree = "";
163 | };
164 | 23E5972F2547D4460080BE0E = {
165 | isa = PBXGroup;
166 | children = (
167 | 23D29C4F256C8DDC00E5CBC0 /* DSFDropFilesView */,
168 | 23E5973A2547D4460080BE0E /* DSFDropFilesView Demo */,
169 | 235981A6256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */,
170 | 230A4B76256CA750009B4689 /* DSFDropFilesView ObjC Demo */,
171 | 23E597392547D4460080BE0E /* Products */,
172 | 23E597522547D56D0080BE0E /* Frameworks */,
173 | );
174 | sourceTree = "";
175 | };
176 | 23E597392547D4460080BE0E /* Products */ = {
177 | isa = PBXGroup;
178 | children = (
179 | 23E597382547D4460080BE0E /* DSFDropFilesView Demo.app */,
180 | 235981A5256B333F000D2479 /* DSFDropFilesView SwiftUI Demo.app */,
181 | 230A4B75256CA750009B4689 /* DSFDropFilesView ObjC Demo.app */,
182 | );
183 | name = Products;
184 | sourceTree = "";
185 | };
186 | 23E5973A2547D4460080BE0E /* DSFDropFilesView Demo */ = {
187 | isa = PBXGroup;
188 | children = (
189 | 23E5973B2547D4460080BE0E /* AppDelegate.swift */,
190 | 23E5973D2547D4460080BE0E /* ViewController.swift */,
191 | 23E5973F2547D4470080BE0E /* Assets.xcassets */,
192 | 23E597412547D4470080BE0E /* Main.storyboard */,
193 | 23E597442547D4470080BE0E /* Info.plist */,
194 | 23E597452547D4470080BE0E /* DSFDropFilesView_Demo.entitlements */,
195 | );
196 | path = "DSFDropFilesView Demo";
197 | sourceTree = "";
198 | };
199 | 23E597522547D56D0080BE0E /* Frameworks */ = {
200 | isa = PBXGroup;
201 | children = (
202 | );
203 | name = Frameworks;
204 | sourceTree = "";
205 | };
206 | /* End PBXGroup section */
207 |
208 | /* Begin PBXNativeTarget section */
209 | 230A4B74256CA750009B4689 /* DSFDropFilesView ObjC Demo */ = {
210 | isa = PBXNativeTarget;
211 | buildConfigurationList = 230A4B88256CA751009B4689 /* Build configuration list for PBXNativeTarget "DSFDropFilesView ObjC Demo" */;
212 | buildPhases = (
213 | 230A4B71256CA750009B4689 /* Sources */,
214 | 230A4B72256CA750009B4689 /* Frameworks */,
215 | 230A4B73256CA750009B4689 /* Resources */,
216 | );
217 | buildRules = (
218 | );
219 | dependencies = (
220 | 230A4B94256CA76B009B4689 /* PBXTargetDependency */,
221 | );
222 | name = "DSFDropFilesView ObjC Demo";
223 | packageProductDependencies = (
224 | 23D18D0D286FA4DF00D5A0D2 /* DSFDropFilesView */,
225 | );
226 | productName = "DSFDropFilesView ObjC Demo";
227 | productReference = 230A4B75256CA750009B4689 /* DSFDropFilesView ObjC Demo.app */;
228 | productType = "com.apple.product-type.application";
229 | };
230 | 235981A4256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */ = {
231 | isa = PBXNativeTarget;
232 | buildConfigurationList = 235981B5256B3340000D2479 /* Build configuration list for PBXNativeTarget "DSFDropFilesView SwiftUI Demo" */;
233 | buildPhases = (
234 | 235981A1256B333F000D2479 /* Sources */,
235 | 235981A2256B333F000D2479 /* Frameworks */,
236 | 235981A3256B333F000D2479 /* Resources */,
237 | );
238 | buildRules = (
239 | );
240 | dependencies = (
241 | 23D29C57256C8DF300E5CBC0 /* PBXTargetDependency */,
242 | );
243 | name = "DSFDropFilesView SwiftUI Demo";
244 | packageProductDependencies = (
245 | 23D18D0B286FA4C000D5A0D2 /* DSFDropFilesView */,
246 | );
247 | productName = "DSFDropFilesView SwiftUI Demo";
248 | productReference = 235981A5256B333F000D2479 /* DSFDropFilesView SwiftUI Demo.app */;
249 | productType = "com.apple.product-type.application";
250 | };
251 | 23E597372547D4460080BE0E /* DSFDropFilesView Demo */ = {
252 | isa = PBXNativeTarget;
253 | buildConfigurationList = 23E597482547D4470080BE0E /* Build configuration list for PBXNativeTarget "DSFDropFilesView Demo" */;
254 | buildPhases = (
255 | 23E597342547D4460080BE0E /* Sources */,
256 | 23E597352547D4460080BE0E /* Frameworks */,
257 | 23E597362547D4460080BE0E /* Resources */,
258 | );
259 | buildRules = (
260 | );
261 | dependencies = (
262 | );
263 | name = "DSFDropFilesView Demo";
264 | packageProductDependencies = (
265 | 23D18D09286FA4B500D5A0D2 /* DSFDropFilesView */,
266 | );
267 | productName = "DSFDropFilesView Demo";
268 | productReference = 23E597382547D4460080BE0E /* DSFDropFilesView Demo.app */;
269 | productType = "com.apple.product-type.application";
270 | };
271 | /* End PBXNativeTarget section */
272 |
273 | /* Begin PBXProject section */
274 | 23E597302547D4460080BE0E /* Project object */ = {
275 | isa = PBXProject;
276 | attributes = {
277 | LastSwiftUpdateCheck = 1220;
278 | LastUpgradeCheck = 1220;
279 | TargetAttributes = {
280 | 230A4B74256CA750009B4689 = {
281 | CreatedOnToolsVersion = 12.2;
282 | };
283 | 235981A4256B333F000D2479 = {
284 | CreatedOnToolsVersion = 12.2;
285 | };
286 | 23D29C41256C890300E5CBC0 = {
287 | CreatedOnToolsVersion = 12.2;
288 | };
289 | 23E597372547D4460080BE0E = {
290 | CreatedOnToolsVersion = 12.2;
291 | };
292 | };
293 | };
294 | buildConfigurationList = 23E597332547D4460080BE0E /* Build configuration list for PBXProject "DSFDropFilesView Demo" */;
295 | compatibilityVersion = "Xcode 9.3";
296 | developmentRegion = en;
297 | hasScannedForEncodings = 0;
298 | knownRegions = (
299 | en,
300 | Base,
301 | );
302 | mainGroup = 23E5972F2547D4460080BE0E;
303 | productRefGroup = 23E597392547D4460080BE0E /* Products */;
304 | projectDirPath = "";
305 | projectRoot = "";
306 | targets = (
307 | 23E597372547D4460080BE0E /* DSFDropFilesView Demo */,
308 | 235981A4256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */,
309 | 23D29C41256C890300E5CBC0 /* All */,
310 | 230A4B74256CA750009B4689 /* DSFDropFilesView ObjC Demo */,
311 | );
312 | };
313 | /* End PBXProject section */
314 |
315 | /* Begin PBXResourcesBuildPhase section */
316 | 230A4B73256CA750009B4689 /* Resources */ = {
317 | isa = PBXResourcesBuildPhase;
318 | buildActionMask = 2147483647;
319 | files = (
320 | 230A4B7E256CA751009B4689 /* Assets.xcassets in Resources */,
321 | 230A4B81256CA751009B4689 /* Main.storyboard in Resources */,
322 | );
323 | runOnlyForDeploymentPostprocessing = 0;
324 | };
325 | 235981A3256B333F000D2479 /* Resources */ = {
326 | isa = PBXResourcesBuildPhase;
327 | buildActionMask = 2147483647;
328 | files = (
329 | 235981B2256B3340000D2479 /* Main.storyboard in Resources */,
330 | 235981AF256B3340000D2479 /* Preview Assets.xcassets in Resources */,
331 | 235981AC256B3340000D2479 /* Assets.xcassets in Resources */,
332 | );
333 | runOnlyForDeploymentPostprocessing = 0;
334 | };
335 | 23E597362547D4460080BE0E /* Resources */ = {
336 | isa = PBXResourcesBuildPhase;
337 | buildActionMask = 2147483647;
338 | files = (
339 | 23E597402547D4470080BE0E /* Assets.xcassets in Resources */,
340 | 23E597432547D4470080BE0E /* Main.storyboard in Resources */,
341 | );
342 | runOnlyForDeploymentPostprocessing = 0;
343 | };
344 | /* End PBXResourcesBuildPhase section */
345 |
346 | /* Begin PBXSourcesBuildPhase section */
347 | 230A4B71256CA750009B4689 /* Sources */ = {
348 | isa = PBXSourcesBuildPhase;
349 | buildActionMask = 2147483647;
350 | files = (
351 | 230A4B7C256CA750009B4689 /* ViewController.m in Sources */,
352 | 230A4B84256CA751009B4689 /* main.m in Sources */,
353 | 230A4B79256CA750009B4689 /* AppDelegate.m in Sources */,
354 | );
355 | runOnlyForDeploymentPostprocessing = 0;
356 | };
357 | 235981A1256B333F000D2479 /* Sources */ = {
358 | isa = PBXSourcesBuildPhase;
359 | buildActionMask = 2147483647;
360 | files = (
361 | 235981AA256B333F000D2479 /* ContentView.swift in Sources */,
362 | 235981A8256B333F000D2479 /* AppDelegate.swift in Sources */,
363 | );
364 | runOnlyForDeploymentPostprocessing = 0;
365 | };
366 | 23E597342547D4460080BE0E /* Sources */ = {
367 | isa = PBXSourcesBuildPhase;
368 | buildActionMask = 2147483647;
369 | files = (
370 | 23E5973E2547D4460080BE0E /* ViewController.swift in Sources */,
371 | 23E5973C2547D4460080BE0E /* AppDelegate.swift in Sources */,
372 | );
373 | runOnlyForDeploymentPostprocessing = 0;
374 | };
375 | /* End PBXSourcesBuildPhase section */
376 |
377 | /* Begin PBXTargetDependency section */
378 | 230A4B8E256CA760009B4689 /* PBXTargetDependency */ = {
379 | isa = PBXTargetDependency;
380 | target = 230A4B74256CA750009B4689 /* DSFDropFilesView ObjC Demo */;
381 | targetProxy = 230A4B8D256CA760009B4689 /* PBXContainerItemProxy */;
382 | };
383 | 230A4B94256CA76B009B4689 /* PBXTargetDependency */ = {
384 | isa = PBXTargetDependency;
385 | productRef = 230A4B93256CA76B009B4689 /* DSFDropFilesView */;
386 | };
387 | 23D29C49256C890800E5CBC0 /* PBXTargetDependency */ = {
388 | isa = PBXTargetDependency;
389 | target = 23E597372547D4460080BE0E /* DSFDropFilesView Demo */;
390 | targetProxy = 23D29C48256C890800E5CBC0 /* PBXContainerItemProxy */;
391 | };
392 | 23D29C4B256C890800E5CBC0 /* PBXTargetDependency */ = {
393 | isa = PBXTargetDependency;
394 | target = 235981A4256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */;
395 | targetProxy = 23D29C4A256C890800E5CBC0 /* PBXContainerItemProxy */;
396 | };
397 | 23D29C57256C8DF300E5CBC0 /* PBXTargetDependency */ = {
398 | isa = PBXTargetDependency;
399 | productRef = 23D29C56256C8DF300E5CBC0 /* DSFDropFilesView */;
400 | };
401 | /* End PBXTargetDependency section */
402 |
403 | /* Begin PBXVariantGroup section */
404 | 230A4B7F256CA751009B4689 /* Main.storyboard */ = {
405 | isa = PBXVariantGroup;
406 | children = (
407 | 230A4B80256CA751009B4689 /* Base */,
408 | );
409 | name = Main.storyboard;
410 | sourceTree = "";
411 | };
412 | 235981B0256B3340000D2479 /* Main.storyboard */ = {
413 | isa = PBXVariantGroup;
414 | children = (
415 | 235981B1256B3340000D2479 /* Base */,
416 | );
417 | name = Main.storyboard;
418 | sourceTree = "";
419 | };
420 | 23E597412547D4470080BE0E /* Main.storyboard */ = {
421 | isa = PBXVariantGroup;
422 | children = (
423 | 23E597422547D4470080BE0E /* Base */,
424 | );
425 | name = Main.storyboard;
426 | sourceTree = "";
427 | };
428 | /* End PBXVariantGroup section */
429 |
430 | /* Begin XCBuildConfiguration section */
431 | 230A4B86256CA751009B4689 /* Debug */ = {
432 | isa = XCBuildConfiguration;
433 | buildSettings = {
434 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
436 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
437 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
438 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView ObjC Demo/DSFDropFilesView_ObjC_Demo.entitlements";
439 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
440 | CODE_SIGN_STYLE = Automatic;
441 | COMBINE_HIDPI_IMAGES = YES;
442 | DEVELOPMENT_TEAM = 3L6RK3LGGW;
443 | ENABLE_HARDENED_RUNTIME = YES;
444 | INFOPLIST_FILE = "DSFDropFilesView ObjC Demo/Info.plist";
445 | LD_RUNPATH_SEARCH_PATHS = (
446 | "$(inherited)",
447 | "@executable_path/../Frameworks",
448 | );
449 | MACOSX_DEPLOYMENT_TARGET = 10.13;
450 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-ObjC-Demo";
451 | PRODUCT_NAME = "$(TARGET_NAME)";
452 | };
453 | name = Debug;
454 | };
455 | 230A4B87256CA751009B4689 /* Release */ = {
456 | isa = XCBuildConfiguration;
457 | buildSettings = {
458 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
459 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
460 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
461 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
462 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView ObjC Demo/DSFDropFilesView_ObjC_Demo.entitlements";
463 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
464 | CODE_SIGN_STYLE = Automatic;
465 | COMBINE_HIDPI_IMAGES = YES;
466 | DEVELOPMENT_TEAM = 3L6RK3LGGW;
467 | ENABLE_HARDENED_RUNTIME = YES;
468 | INFOPLIST_FILE = "DSFDropFilesView ObjC Demo/Info.plist";
469 | LD_RUNPATH_SEARCH_PATHS = (
470 | "$(inherited)",
471 | "@executable_path/../Frameworks",
472 | );
473 | MACOSX_DEPLOYMENT_TARGET = 10.13;
474 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-ObjC-Demo";
475 | PRODUCT_NAME = "$(TARGET_NAME)";
476 | };
477 | name = Release;
478 | };
479 | 235981B6256B3340000D2479 /* Debug */ = {
480 | isa = XCBuildConfiguration;
481 | buildSettings = {
482 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
483 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
484 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView SwiftUI Demo/DSFDropFilesView_SwiftUI_Demo.entitlements";
485 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
486 | CODE_SIGN_STYLE = Automatic;
487 | COMBINE_HIDPI_IMAGES = YES;
488 | DEVELOPMENT_ASSET_PATHS = "\"DSFDropFilesView SwiftUI Demo/Preview Content\"";
489 | DEVELOPMENT_TEAM = 3L6RK3LGGW;
490 | ENABLE_HARDENED_RUNTIME = YES;
491 | ENABLE_PREVIEWS = YES;
492 | INFOPLIST_FILE = "DSFDropFilesView SwiftUI Demo/Info.plist";
493 | LD_RUNPATH_SEARCH_PATHS = (
494 | "$(inherited)",
495 | "@executable_path/../Frameworks",
496 | );
497 | MACOSX_DEPLOYMENT_TARGET = 11.0;
498 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-SwiftUI-Demo";
499 | PRODUCT_NAME = "$(TARGET_NAME)";
500 | SWIFT_VERSION = 5.0;
501 | };
502 | name = Debug;
503 | };
504 | 235981B7256B3340000D2479 /* Release */ = {
505 | isa = XCBuildConfiguration;
506 | buildSettings = {
507 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
508 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
509 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView SwiftUI Demo/DSFDropFilesView_SwiftUI_Demo.entitlements";
510 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
511 | CODE_SIGN_STYLE = Automatic;
512 | COMBINE_HIDPI_IMAGES = YES;
513 | DEVELOPMENT_ASSET_PATHS = "\"DSFDropFilesView SwiftUI Demo/Preview Content\"";
514 | DEVELOPMENT_TEAM = 3L6RK3LGGW;
515 | ENABLE_HARDENED_RUNTIME = YES;
516 | ENABLE_PREVIEWS = YES;
517 | INFOPLIST_FILE = "DSFDropFilesView SwiftUI Demo/Info.plist";
518 | LD_RUNPATH_SEARCH_PATHS = (
519 | "$(inherited)",
520 | "@executable_path/../Frameworks",
521 | );
522 | MACOSX_DEPLOYMENT_TARGET = 11.0;
523 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-SwiftUI-Demo";
524 | PRODUCT_NAME = "$(TARGET_NAME)";
525 | SWIFT_VERSION = 5.0;
526 | };
527 | name = Release;
528 | };
529 | 23D29C42256C890300E5CBC0 /* Debug */ = {
530 | isa = XCBuildConfiguration;
531 | buildSettings = {
532 | CODE_SIGN_STYLE = Automatic;
533 | DEVELOPMENT_TEAM = 3L6RK3LGGW;
534 | PRODUCT_NAME = "$(TARGET_NAME)";
535 | };
536 | name = Debug;
537 | };
538 | 23D29C43256C890300E5CBC0 /* Release */ = {
539 | isa = XCBuildConfiguration;
540 | buildSettings = {
541 | CODE_SIGN_STYLE = Automatic;
542 | DEVELOPMENT_TEAM = 3L6RK3LGGW;
543 | PRODUCT_NAME = "$(TARGET_NAME)";
544 | };
545 | name = Release;
546 | };
547 | 23E597462547D4470080BE0E /* Debug */ = {
548 | isa = XCBuildConfiguration;
549 | buildSettings = {
550 | ALWAYS_SEARCH_USER_PATHS = NO;
551 | CLANG_ANALYZER_NONNULL = YES;
552 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
553 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
554 | CLANG_CXX_LIBRARY = "libc++";
555 | CLANG_ENABLE_MODULES = YES;
556 | CLANG_ENABLE_OBJC_ARC = YES;
557 | CLANG_ENABLE_OBJC_WEAK = YES;
558 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
559 | CLANG_WARN_BOOL_CONVERSION = YES;
560 | CLANG_WARN_COMMA = YES;
561 | CLANG_WARN_CONSTANT_CONVERSION = YES;
562 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
563 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
564 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
565 | CLANG_WARN_EMPTY_BODY = YES;
566 | CLANG_WARN_ENUM_CONVERSION = YES;
567 | CLANG_WARN_INFINITE_RECURSION = YES;
568 | CLANG_WARN_INT_CONVERSION = YES;
569 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
570 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
571 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
572 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
573 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
574 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
575 | CLANG_WARN_STRICT_PROTOTYPES = YES;
576 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
577 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
578 | CLANG_WARN_UNREACHABLE_CODE = YES;
579 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
580 | COPY_PHASE_STRIP = NO;
581 | DEBUG_INFORMATION_FORMAT = dwarf;
582 | ENABLE_STRICT_OBJC_MSGSEND = YES;
583 | ENABLE_TESTABILITY = YES;
584 | GCC_C_LANGUAGE_STANDARD = gnu11;
585 | GCC_DYNAMIC_NO_PIC = NO;
586 | GCC_NO_COMMON_BLOCKS = YES;
587 | GCC_OPTIMIZATION_LEVEL = 0;
588 | GCC_PREPROCESSOR_DEFINITIONS = (
589 | "DEBUG=1",
590 | "$(inherited)",
591 | );
592 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
593 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
594 | GCC_WARN_UNDECLARED_SELECTOR = YES;
595 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
596 | GCC_WARN_UNUSED_FUNCTION = YES;
597 | GCC_WARN_UNUSED_VARIABLE = YES;
598 | MACOSX_DEPLOYMENT_TARGET = 10.15;
599 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
600 | MTL_FAST_MATH = YES;
601 | ONLY_ACTIVE_ARCH = YES;
602 | SDKROOT = macosx;
603 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
604 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
605 | };
606 | name = Debug;
607 | };
608 | 23E597472547D4470080BE0E /* Release */ = {
609 | isa = XCBuildConfiguration;
610 | buildSettings = {
611 | ALWAYS_SEARCH_USER_PATHS = NO;
612 | CLANG_ANALYZER_NONNULL = YES;
613 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
614 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
615 | CLANG_CXX_LIBRARY = "libc++";
616 | CLANG_ENABLE_MODULES = YES;
617 | CLANG_ENABLE_OBJC_ARC = YES;
618 | CLANG_ENABLE_OBJC_WEAK = YES;
619 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
620 | CLANG_WARN_BOOL_CONVERSION = YES;
621 | CLANG_WARN_COMMA = YES;
622 | CLANG_WARN_CONSTANT_CONVERSION = YES;
623 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
624 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
625 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
626 | CLANG_WARN_EMPTY_BODY = YES;
627 | CLANG_WARN_ENUM_CONVERSION = YES;
628 | CLANG_WARN_INFINITE_RECURSION = YES;
629 | CLANG_WARN_INT_CONVERSION = YES;
630 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
631 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
632 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
633 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
634 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
635 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
636 | CLANG_WARN_STRICT_PROTOTYPES = YES;
637 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
638 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
639 | CLANG_WARN_UNREACHABLE_CODE = YES;
640 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
641 | COPY_PHASE_STRIP = NO;
642 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
643 | ENABLE_NS_ASSERTIONS = NO;
644 | ENABLE_STRICT_OBJC_MSGSEND = YES;
645 | GCC_C_LANGUAGE_STANDARD = gnu11;
646 | GCC_NO_COMMON_BLOCKS = YES;
647 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
648 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
649 | GCC_WARN_UNDECLARED_SELECTOR = YES;
650 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
651 | GCC_WARN_UNUSED_FUNCTION = YES;
652 | GCC_WARN_UNUSED_VARIABLE = YES;
653 | MACOSX_DEPLOYMENT_TARGET = 10.15;
654 | MTL_ENABLE_DEBUG_INFO = NO;
655 | MTL_FAST_MATH = YES;
656 | SDKROOT = macosx;
657 | SWIFT_COMPILATION_MODE = wholemodule;
658 | SWIFT_OPTIMIZATION_LEVEL = "-O";
659 | };
660 | name = Release;
661 | };
662 | 23E597492547D4470080BE0E /* Debug */ = {
663 | isa = XCBuildConfiguration;
664 | buildSettings = {
665 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
666 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
667 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView Demo/DSFDropFilesView_Demo.entitlements";
668 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
669 | CODE_SIGN_STYLE = Automatic;
670 | COMBINE_HIDPI_IMAGES = YES;
671 | DEVELOPMENT_TEAM = 3L6RK3LGGW;
672 | ENABLE_HARDENED_RUNTIME = YES;
673 | INFOPLIST_FILE = "DSFDropFilesView Demo/Info.plist";
674 | LD_RUNPATH_SEARCH_PATHS = (
675 | "$(inherited)",
676 | "@executable_path/../Frameworks",
677 | );
678 | MACOSX_DEPLOYMENT_TARGET = 10.13;
679 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-Demo";
680 | PRODUCT_NAME = "$(TARGET_NAME)";
681 | SWIFT_VERSION = 5.0;
682 | };
683 | name = Debug;
684 | };
685 | 23E5974A2547D4470080BE0E /* Release */ = {
686 | isa = XCBuildConfiguration;
687 | buildSettings = {
688 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
689 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
690 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView Demo/DSFDropFilesView_Demo.entitlements";
691 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
692 | CODE_SIGN_STYLE = Automatic;
693 | COMBINE_HIDPI_IMAGES = YES;
694 | DEVELOPMENT_TEAM = 3L6RK3LGGW;
695 | ENABLE_HARDENED_RUNTIME = YES;
696 | INFOPLIST_FILE = "DSFDropFilesView Demo/Info.plist";
697 | LD_RUNPATH_SEARCH_PATHS = (
698 | "$(inherited)",
699 | "@executable_path/../Frameworks",
700 | );
701 | MACOSX_DEPLOYMENT_TARGET = 10.13;
702 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-Demo";
703 | PRODUCT_NAME = "$(TARGET_NAME)";
704 | SWIFT_VERSION = 5.0;
705 | };
706 | name = Release;
707 | };
708 | /* End XCBuildConfiguration section */
709 |
710 | /* Begin XCConfigurationList section */
711 | 230A4B88256CA751009B4689 /* Build configuration list for PBXNativeTarget "DSFDropFilesView ObjC Demo" */ = {
712 | isa = XCConfigurationList;
713 | buildConfigurations = (
714 | 230A4B86256CA751009B4689 /* Debug */,
715 | 230A4B87256CA751009B4689 /* Release */,
716 | );
717 | defaultConfigurationIsVisible = 0;
718 | defaultConfigurationName = Release;
719 | };
720 | 235981B5256B3340000D2479 /* Build configuration list for PBXNativeTarget "DSFDropFilesView SwiftUI Demo" */ = {
721 | isa = XCConfigurationList;
722 | buildConfigurations = (
723 | 235981B6256B3340000D2479 /* Debug */,
724 | 235981B7256B3340000D2479 /* Release */,
725 | );
726 | defaultConfigurationIsVisible = 0;
727 | defaultConfigurationName = Release;
728 | };
729 | 23D29C44256C890300E5CBC0 /* Build configuration list for PBXAggregateTarget "All" */ = {
730 | isa = XCConfigurationList;
731 | buildConfigurations = (
732 | 23D29C42256C890300E5CBC0 /* Debug */,
733 | 23D29C43256C890300E5CBC0 /* Release */,
734 | );
735 | defaultConfigurationIsVisible = 0;
736 | defaultConfigurationName = Release;
737 | };
738 | 23E597332547D4460080BE0E /* Build configuration list for PBXProject "DSFDropFilesView Demo" */ = {
739 | isa = XCConfigurationList;
740 | buildConfigurations = (
741 | 23E597462547D4470080BE0E /* Debug */,
742 | 23E597472547D4470080BE0E /* Release */,
743 | );
744 | defaultConfigurationIsVisible = 0;
745 | defaultConfigurationName = Release;
746 | };
747 | 23E597482547D4470080BE0E /* Build configuration list for PBXNativeTarget "DSFDropFilesView Demo" */ = {
748 | isa = XCConfigurationList;
749 | buildConfigurations = (
750 | 23E597492547D4470080BE0E /* Debug */,
751 | 23E5974A2547D4470080BE0E /* Release */,
752 | );
753 | defaultConfigurationIsVisible = 0;
754 | defaultConfigurationName = Release;
755 | };
756 | /* End XCConfigurationList section */
757 |
758 | /* Begin XCSwiftPackageProductDependency section */
759 | 230A4B93256CA76B009B4689 /* DSFDropFilesView */ = {
760 | isa = XCSwiftPackageProductDependency;
761 | productName = DSFDropFilesView;
762 | };
763 | 23D18D09286FA4B500D5A0D2 /* DSFDropFilesView */ = {
764 | isa = XCSwiftPackageProductDependency;
765 | productName = DSFDropFilesView;
766 | };
767 | 23D18D0B286FA4C000D5A0D2 /* DSFDropFilesView */ = {
768 | isa = XCSwiftPackageProductDependency;
769 | productName = DSFDropFilesView;
770 | };
771 | 23D18D0D286FA4DF00D5A0D2 /* DSFDropFilesView */ = {
772 | isa = XCSwiftPackageProductDependency;
773 | productName = DSFDropFilesView;
774 | };
775 | 23D29C56256C8DF300E5CBC0 /* DSFDropFilesView */ = {
776 | isa = XCSwiftPackageProductDependency;
777 | productName = DSFDropFilesView;
778 | };
779 | /* End XCSwiftPackageProductDependency section */
780 | };
781 | rootObject = 23E597302547D4460080BE0E /* Project object */;
782 | }
783 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
--------------------------------------------------------------------------------
/Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 | Default
530 |
531 |
532 |
533 |
534 |
535 |
536 | Left to Right
537 |
538 |
539 |
540 |
541 |
542 |
543 | Right to Left
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 | Default
555 |
556 |
557 |
558 |
559 |
560 |
561 | Left to Right
562 |
563 |
564 |
565 |
566 |
567 |
568 | Right to Left
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
--------------------------------------------------------------------------------