├── AppKit Cocoa Resizable NSView
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── main.swift
├── AppKit_Cocoa_Resizable_NSView.entitlements
├── AppDelegate.swift
├── Info.plist
├── ViewController.swift
├── NSViewHelpers.swift
├── ResizableView.swift
└── DraggableResizableView.swift
├── Screenshots
└── BBDragResizableView.gif
├── AppKit Cocoa Resizable NSView.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── fingent.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── README.md
└── .gitignore
/AppKit Cocoa Resizable NSView/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Screenshots/BBDragResizableView.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bibinjacobpulickal/BBDragResizableView/HEAD/Screenshots/BBDragResizableView.gif
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // CocoaTest
4 | //
5 | // Created by Bibin Jacob Pulickal on 21/05/19.
6 | // Copyright © 2019 Bibin Jacob Pulickal. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | let appDelegate = AppDelegate()
12 | NSApplication.shared.delegate = appDelegate
13 | NSApplication.shared.run()
14 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/AppKit_Cocoa_Resizable_NSView.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 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView.xcodeproj/xcuserdata/fingent.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | AppKit Cocoa Resizable NSView.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BBDragResizableView
2 | A sample project to show how to create a draggable and resizable NSView in Cocoa framework in AppKit using Xcode 11.4 on MacOS 10.15 using Swift 5.2
3 |
4 | ---
5 | 
6 | [](https://www.apple.com/in/macos/catalina/)
7 | [](https://developer.apple.com/xcode/)
8 | [](https://developer.apple.com/swift/)
9 |
10 | ## Working Example
11 |
12 |
13 | ## Example
14 | Download the project.
15 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CocoaTest
4 | //
5 | // Created by Bibin Jacob Pulickal on 21/05/19.
6 | // Copyright © 2019 Bibin Jacob Pulickal. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class AppDelegate: NSObject, NSApplicationDelegate {
12 |
13 | var window: NSWindow?
14 |
15 | func applicationDidFinishLaunching(_ aNotification: Notification) {
16 | window = NSWindow(contentViewController: ViewController())
17 | window?.makeKeyAndOrderFront(nil)
18 | setupWindowProperties()
19 | }
20 |
21 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { true }
22 |
23 | func setupWindowProperties() {
24 | window?.toolbar?.showsBaselineSeparator = false
25 | window?.titlebarAppearsTransparent = true
26 | window?.titleVisibility = .hidden
27 | window?.styleMask.insert(.fullSizeContentView)
28 | window?.center()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/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 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2019 Bibin Jacob Pulickal. All rights reserved.
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // AppKit Cocoa Resizable NSView
4 | //
5 | // Created by Bibin Jacob Pulickal on 06/06/19.
6 | // Copyright © 2019 Bibin Jacob Pulickal. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import AutoLayoutProxy
11 |
12 | class ViewController: NSViewController {
13 |
14 | private let draggableResizableView = DraggableResizableView()
15 |
16 | override func loadView() {
17 | view = NSView()
18 | view.size >= 500
19 | view.addSubview(NSButton(title: "RESET", target: self, action: #selector(resetFrame))) {
20 | $0.centerX == $1.centerX
21 | }
22 | view.addSubview(draggableResizableView)
23 | }
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 | perform(#selector(resetFrame), with: nil, afterDelay: 1)
28 | }
29 |
30 | @objc func resetFrame() {
31 | let width = view.bounds.width / 5
32 | let height = view.bounds.height / 5
33 | let x = (view.bounds.width - width) * 0.5
34 | let y = (view.bounds.height - height) * 0.5
35 | draggableResizableView.frame = CGRect(x: x, y: y, width: width, height: height)
36 | draggableResizableView.autoresizingMask = [.minXMargin, .minYMargin, .maxXMargin, .maxYMargin]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | Packages/
41 | Package.pins
42 | Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/NSViewHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSViewHelpers.swift
3 | // IncomingCallPopup
4 | //
5 | // Created by Bibin Jacob Pulickal on 10/05/19.
6 | // Copyright © 2019 Bibin Jacob Pulickal. All rights reserved.
7 | //
8 |
9 | #if canImport(Cocoa)
10 | import Cocoa
11 |
12 | // MARK: - Properties
13 | public extension NSView {
14 |
15 | @IBInspectable
16 | var backgroundColor: NSColor? {
17 | get {
18 | guard let color = layer?.backgroundColor else { return nil }
19 | return NSColor(cgColor: color)
20 | }
21 | set {
22 | wantsLayer = true
23 | layer?.backgroundColor = newValue?.cgColor
24 | }
25 | }
26 |
27 | var origin: CGPoint {
28 | get {
29 | frame.origin
30 | }
31 | set {
32 | frame.origin.x = newValue.x
33 | frame.origin.y = newValue.y
34 | }
35 | }
36 |
37 | var size: CGSize {
38 | get {
39 | frame.size
40 | }
41 | set {
42 | width = newValue.width
43 | height = newValue.height
44 | }
45 | }
46 |
47 | var width: CGFloat {
48 | get {
49 | frame.size.width
50 | }
51 | set {
52 | frame.size.width = newValue
53 | }
54 | }
55 |
56 | var height: CGFloat {
57 | get {
58 | frame.size.height
59 | }
60 | set {
61 | frame.size.height = newValue
62 | }
63 | }
64 |
65 | @IBInspectable
66 | var borderColor: NSColor? {
67 | get {
68 | guard let color = layer?.borderColor else { return nil }
69 | return NSColor(cgColor: color)
70 | }
71 | set {
72 | wantsLayer = true
73 | layer?.borderColor = newValue?.cgColor
74 | }
75 | }
76 |
77 | @IBInspectable
78 | var borderWidth: CGFloat {
79 | get {
80 | layer?.borderWidth ?? 0
81 | }
82 | set {
83 | wantsLayer = true
84 | layer?.borderWidth = newValue
85 | }
86 | }
87 |
88 | @IBInspectable
89 | var cornerRadius: CGFloat {
90 | get {
91 | layer?.cornerRadius ?? 0
92 | }
93 | set {
94 | wantsLayer = true
95 | layer?.masksToBounds = true
96 | layer?.cornerRadius = abs(CGFloat(Int(newValue * 100)) / 100)
97 | }
98 | }
99 |
100 | @IBInspectable
101 | var shadowColor: NSColor? {
102 | get {
103 | guard let color = layer?.shadowColor else { return nil }
104 | return NSColor(cgColor: color)
105 | }
106 | set {
107 | wantsLayer = true
108 | layer?.shadowColor = newValue?.cgColor
109 | }
110 | }
111 |
112 | @IBInspectable
113 | var shadowOffset: CGSize {
114 | get {
115 | layer?.shadowOffset ?? CGSize.zero
116 | }
117 | set {
118 | wantsLayer = true
119 | layer?.shadowOffset = newValue
120 | }
121 | }
122 |
123 | @IBInspectable
124 | var shadowOpacity: Float {
125 | get {
126 | layer?.shadowOpacity ?? 0
127 | }
128 | set {
129 | wantsLayer = true
130 | layer?.shadowOpacity = newValue
131 | }
132 | }
133 |
134 | @IBInspectable
135 | var shadowRadius: CGFloat {
136 | get {
137 | return layer?.shadowRadius ?? 0
138 | }
139 | set {
140 | wantsLayer = true
141 | layer?.shadowRadius = newValue
142 | }
143 | }
144 |
145 | func addTrackingRect(_ rect: NSRect) {
146 | addTrackingArea(NSTrackingArea(
147 | rect: rect,
148 | options: [
149 | .mouseMoved,
150 | .mouseEnteredAndExited,
151 | .activeAlways],
152 | owner: self))
153 | }
154 | }
155 |
156 | #endif
157 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/ResizableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ResizableView.swift
3 | // AppKit Cocoa Resizable NSView
4 | //
5 | // Created by Bibin Jacob Pulickal on 06/06/19.
6 | // Copyright © 2019 Bibin Jacob Pulickal. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class ResizableView: NSView {
12 |
13 | private let resizableArea = CGFloat(2)
14 | private var draggedPoint = CGPoint.zero
15 |
16 | override init(frame frameRect: NSRect) {
17 | super.init(frame: frameRect)
18 | backgroundColor = .red
19 | borderColor = .white
20 | borderWidth = resizableArea
21 | }
22 |
23 | override func updateTrackingAreas() {
24 | super.updateTrackingAreas()
25 | trackingAreas.forEach { area in
26 | removeTrackingArea(area)
27 | }
28 | addTrackingRect(bounds)
29 | }
30 |
31 | required init?(coder decoder: NSCoder) {
32 | fatalError("init(coder:) has not been implemented")
33 | }
34 |
35 | override func mouseExited(with event: NSEvent) {
36 | super.mouseExited(with: event)
37 | NSCursor.arrow.set()
38 | }
39 |
40 | override func mouseDown(with event: NSEvent) {
41 | super.mouseDown(with: event)
42 | let locationInView = convert(event.locationInWindow, from: nil)
43 | draggedPoint = locationInView
44 | }
45 |
46 | override func mouseUp(with event: NSEvent) {
47 | super.mouseUp(with: event)
48 | draggedPoint = .zero
49 | }
50 |
51 | override func mouseMoved(with event: NSEvent) {
52 | super.mouseMoved(with: event)
53 | let locationInView = convert(event.locationInWindow, from: nil)
54 | cursorBorderPosition(locationInView)
55 | }
56 |
57 | override func mouseDragged(with event: NSEvent) {
58 | super.mouseDragged(with: event)
59 | borderWidth = resizableArea
60 | let locationInView = convert(event.locationInWindow, from: nil)
61 | let horizontalDistanceDragged = locationInView.x - draggedPoint.x
62 | let verticalDistanceDragged = locationInView.y - draggedPoint.y
63 | let cursorPosition = cursorBorderPosition(draggedPoint)
64 | if cursorPosition != .none {
65 | let drag = CGPoint(x: horizontalDistanceDragged, y: verticalDistanceDragged)
66 | if checkIfBorder(cursorPosition, exceedsSuperviewBorderWithPadding: 12, andDraggedOutward: drag) {
67 | return
68 | }
69 | }
70 | switch cursorPosition {
71 | case .top:
72 | size.height += verticalDistanceDragged
73 | draggedPoint = locationInView
74 | case .left:
75 | origin.x += horizontalDistanceDragged
76 | size.width -= horizontalDistanceDragged
77 | case .bottom:
78 | origin.y += verticalDistanceDragged
79 | size.height -= verticalDistanceDragged
80 | case .right:
81 | size.width += horizontalDistanceDragged
82 | draggedPoint = locationInView
83 | case .none:
84 | break
85 | }
86 | }
87 |
88 | @discardableResult
89 | func cursorBorderPosition(_ locationInView: CGPoint) -> BorderPosition {
90 | if locationInView.x < resizableArea {
91 | NSCursor.resizeLeftRight.set()
92 | return .left
93 | } else if locationInView.x > bounds.width - resizableArea {
94 | NSCursor.resizeLeftRight.set()
95 | return .right
96 | } else if locationInView.y < resizableArea {
97 | NSCursor.resizeUpDown.set()
98 | return .bottom
99 | } else if locationInView.y > bounds.height - resizableArea {
100 | NSCursor.resizeUpDown.set()
101 | return .top
102 | } else {
103 | NSCursor.arrow.set()
104 | return .none
105 | }
106 | }
107 |
108 | private func checkIfBorder(_ border: BorderPosition,
109 | exceedsSuperviewBorderWithPadding padding: CGFloat,
110 | andDraggedOutward drag: CGPoint) -> Bool {
111 | if border == .left && frame.minX <= padding && drag.x < 0 {
112 | return true
113 | }
114 | if border == .bottom && frame.minY <= padding && drag.y < 0 {
115 | return true
116 | }
117 | guard let superView = superview else { return false }
118 | if border == .right && frame.maxX >= superView.frame.maxX - padding && drag.x > 0 {
119 | return true
120 | }
121 | if border == .top && frame.maxY >= superView.frame.maxY - padding && drag.y > 0 {
122 | return true
123 | }
124 | return false
125 | }
126 |
127 | enum BorderPosition {
128 | case top, left, bottom, right, none
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView/DraggableResizableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DraggableResizableView.swift
3 | // AppKit Cocoa Resizable NSView
4 | //
5 | // Created by Bibin Jacob Pulickal on 11/06/19.
6 | // Copyright © 2019 Bibin Jacob Pulickal. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class DraggableResizableView: NSView {
12 |
13 | private let resizableArea: CGFloat = 4
14 | private let borderPadding: CGFloat = 32
15 | private var draggedPoint: CGPoint = .zero
16 |
17 | override init(frame frameRect: NSRect) {
18 | super.init(frame: frameRect)
19 | backgroundColor = .red
20 | }
21 |
22 | override func updateTrackingAreas() {
23 | super.updateTrackingAreas()
24 | trackingAreas.forEach { area in
25 | removeTrackingArea(area)
26 | }
27 | addTrackingRect(bounds)
28 | }
29 |
30 | required init?(coder decoder: NSCoder) {
31 | fatalError("init(coder:) has not been implemented")
32 | }
33 |
34 | override func mouseEntered(with event: NSEvent) {
35 | super.mouseEntered(with: event)
36 | backgroundColor = .green
37 | borderColor = .white
38 | borderWidth = 1
39 | }
40 |
41 | override func mouseExited(with event: NSEvent) {
42 | super.mouseExited(with: event)
43 | NSCursor.arrow.set()
44 | backgroundColor = .red
45 | borderColor = .clear
46 | borderWidth = 0
47 | }
48 |
49 | override func mouseDown(with event: NSEvent) {
50 | super.mouseDown(with: event)
51 | let locationInView = convert(event.locationInWindow, from: nil)
52 | draggedPoint = locationInView
53 | }
54 |
55 | override func mouseUp(with event: NSEvent) {
56 | super.mouseUp(with: event)
57 | draggedPoint = .zero
58 | }
59 |
60 | override func mouseMoved(with event: NSEvent) {
61 | super.mouseMoved(with: event)
62 | let locationInView = convert(event.locationInWindow, from: nil)
63 | cursorBorderPosition(locationInView)
64 | }
65 |
66 | override func mouseDragged(with event: NSEvent) {
67 | super.mouseDragged(with: event)
68 | backgroundColor = .green
69 | borderWidth = 1
70 | let locationInView = convert(event.locationInWindow, from: nil)
71 | let horizontalDistanceDragged = locationInView.x - draggedPoint.x
72 | let verticalDistanceDragged = locationInView.y - draggedPoint.y
73 | let cursorPosition = cursorBorderPosition(draggedPoint)
74 | if cursorPosition != .none {
75 | let drag = CGPoint(x: horizontalDistanceDragged, y: verticalDistanceDragged)
76 | if checkIfBorder(cursorPosition, andDraggedOutward: drag) {
77 | return
78 | }
79 | }
80 | switch cursorPosition {
81 | case .top:
82 | size.height += verticalDistanceDragged
83 | draggedPoint = locationInView
84 | case .left:
85 | origin.x += horizontalDistanceDragged
86 | size.width -= horizontalDistanceDragged
87 | case .bottom:
88 | origin.y += verticalDistanceDragged
89 | size.height -= verticalDistanceDragged
90 | case .right:
91 | size.width += horizontalDistanceDragged
92 | draggedPoint = locationInView
93 | case .none:
94 | origin.x += locationInView.x - draggedPoint.x
95 | origin.y += locationInView.y - draggedPoint.y
96 | repositionView()
97 | }
98 | }
99 |
100 | @discardableResult
101 | func cursorBorderPosition(_ locationInView: CGPoint) -> BorderPosition {
102 | if locationInView.x < resizableArea {
103 | NSCursor.resizeLeftRight.set()
104 | return .left
105 | } else if locationInView.x > bounds.width - resizableArea {
106 | NSCursor.resizeLeftRight.set()
107 | return .right
108 | } else if locationInView.y < resizableArea {
109 | NSCursor.resizeUpDown.set()
110 | return .bottom
111 | } else if locationInView.y > bounds.height - resizableArea {
112 | NSCursor.resizeUpDown.set()
113 | return .top
114 | } else {
115 | NSCursor.arrow.set()
116 | return .none
117 | }
118 | }
119 |
120 | enum BorderPosition {
121 | case top, left, bottom, right, none
122 | }
123 |
124 | private func checkIfBorder(_ border: BorderPosition,
125 | andDraggedOutward drag: CGPoint) -> Bool {
126 | if border == .left && frame.minX <= borderPadding && drag.x < 0 {
127 | return true
128 | }
129 | if border == .bottom && frame.minY <= borderPadding && drag.y < 0 {
130 | return true
131 | }
132 | guard let superView = superview else { return false }
133 | if border == .right && frame.maxX >= superView.frame.maxX - borderPadding && drag.x > 0 {
134 | return true
135 | }
136 | if border == .top && frame.maxY >= superView.frame.maxY - borderPadding && drag.y > 0 {
137 | return true
138 | }
139 | return false
140 | }
141 |
142 | private func repositionView() {
143 | if frame.minX < borderPadding {
144 | origin.x = borderPadding
145 | }
146 | if frame.minY < borderPadding {
147 | origin.y = borderPadding
148 | }
149 | guard let superView = superview else { return }
150 | if frame.maxX > superView.frame.maxX - borderPadding {
151 | origin.x = superView.frame.maxX - frame.width - borderPadding
152 | }
153 | if frame.maxY > superView.frame.maxY - borderPadding {
154 | origin.y = superView.frame.maxY - frame.height - borderPadding
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/AppKit Cocoa Resizable NSView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | AD2713E922A94A72004A042C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AD2713E822A94A72004A042C /* Assets.xcassets */; };
11 | AD2713F922A94AB0004A042C /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2713F522A94AAF004A042C /* main.swift */; };
12 | AD2713FA22A94AB0004A042C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2713F622A94AAF004A042C /* AppDelegate.swift */; };
13 | AD2713FB22A94AB0004A042C /* NSViewHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2713F722A94AAF004A042C /* NSViewHelpers.swift */; };
14 | AD2713FD22A94AEF004A042C /* ResizableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2713FC22A94AEF004A042C /* ResizableView.swift */; };
15 | AD2713FF22A94B11004A042C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD2713FE22A94B11004A042C /* ViewController.swift */; };
16 | AD27145822AF69C7004A042C /* DraggableResizableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD27145722AF69C7004A042C /* DraggableResizableView.swift */; };
17 | EFB48DA823E556ED00D1D769 /* AutoLayoutProxy in Frameworks */ = {isa = PBXBuildFile; productRef = EFB48DA723E556ED00D1D769 /* AutoLayoutProxy */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | AD2713E322A94A70004A042C /* AppKit Cocoa Resizable NSView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AppKit Cocoa Resizable NSView.app"; sourceTree = BUILT_PRODUCTS_DIR; };
22 | AD2713E822A94A72004A042C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
23 | AD2713ED22A94A72004A042C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
24 | AD2713EE22A94A72004A042C /* AppKit_Cocoa_Resizable_NSView.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AppKit_Cocoa_Resizable_NSView.entitlements; sourceTree = ""; };
25 | AD2713F522A94AAF004A042C /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
26 | AD2713F622A94AAF004A042C /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
27 | AD2713F722A94AAF004A042C /* NSViewHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSViewHelpers.swift; sourceTree = ""; };
28 | AD2713FC22A94AEF004A042C /* ResizableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableView.swift; sourceTree = ""; };
29 | AD2713FE22A94B11004A042C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
30 | AD27145722AF69C7004A042C /* DraggableResizableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableResizableView.swift; sourceTree = ""; };
31 | /* End PBXFileReference section */
32 |
33 | /* Begin PBXFrameworksBuildPhase section */
34 | AD2713E022A94A70004A042C /* Frameworks */ = {
35 | isa = PBXFrameworksBuildPhase;
36 | buildActionMask = 2147483647;
37 | files = (
38 | EFB48DA823E556ED00D1D769 /* AutoLayoutProxy in Frameworks */,
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXFrameworksBuildPhase section */
43 |
44 | /* Begin PBXGroup section */
45 | AD2713DA22A94A70004A042C = {
46 | isa = PBXGroup;
47 | children = (
48 | AD2713E522A94A70004A042C /* AppKit Cocoa Resizable NSView */,
49 | AD2713E422A94A70004A042C /* Products */,
50 | );
51 | sourceTree = "";
52 | };
53 | AD2713E422A94A70004A042C /* Products */ = {
54 | isa = PBXGroup;
55 | children = (
56 | AD2713E322A94A70004A042C /* AppKit Cocoa Resizable NSView.app */,
57 | );
58 | name = Products;
59 | sourceTree = "";
60 | };
61 | AD2713E522A94A70004A042C /* AppKit Cocoa Resizable NSView */ = {
62 | isa = PBXGroup;
63 | children = (
64 | AD2713F522A94AAF004A042C /* main.swift */,
65 | AD2713F622A94AAF004A042C /* AppDelegate.swift */,
66 | AD2713FE22A94B11004A042C /* ViewController.swift */,
67 | AD2713FC22A94AEF004A042C /* ResizableView.swift */,
68 | AD27145722AF69C7004A042C /* DraggableResizableView.swift */,
69 | AD2713F722A94AAF004A042C /* NSViewHelpers.swift */,
70 | AD2713E822A94A72004A042C /* Assets.xcassets */,
71 | AD2713ED22A94A72004A042C /* Info.plist */,
72 | AD2713EE22A94A72004A042C /* AppKit_Cocoa_Resizable_NSView.entitlements */,
73 | );
74 | path = "AppKit Cocoa Resizable NSView";
75 | sourceTree = "";
76 | };
77 | /* End PBXGroup section */
78 |
79 | /* Begin PBXNativeTarget section */
80 | AD2713E222A94A70004A042C /* AppKit Cocoa Resizable NSView */ = {
81 | isa = PBXNativeTarget;
82 | buildConfigurationList = AD2713F122A94A72004A042C /* Build configuration list for PBXNativeTarget "AppKit Cocoa Resizable NSView" */;
83 | buildPhases = (
84 | AD2713DF22A94A70004A042C /* Sources */,
85 | AD2713E022A94A70004A042C /* Frameworks */,
86 | AD2713E122A94A70004A042C /* Resources */,
87 | );
88 | buildRules = (
89 | );
90 | dependencies = (
91 | );
92 | name = "AppKit Cocoa Resizable NSView";
93 | packageProductDependencies = (
94 | EFB48DA723E556ED00D1D769 /* AutoLayoutProxy */,
95 | );
96 | productName = "AppKit Cocoa Resizable NSView";
97 | productReference = AD2713E322A94A70004A042C /* AppKit Cocoa Resizable NSView.app */;
98 | productType = "com.apple.product-type.application";
99 | };
100 | /* End PBXNativeTarget section */
101 |
102 | /* Begin PBXProject section */
103 | AD2713DB22A94A70004A042C /* Project object */ = {
104 | isa = PBXProject;
105 | attributes = {
106 | LastSwiftUpdateCheck = 1010;
107 | LastUpgradeCheck = 1100;
108 | ORGANIZATIONNAME = "Bibin Jacob Pulickal";
109 | TargetAttributes = {
110 | AD2713E222A94A70004A042C = {
111 | CreatedOnToolsVersion = 10.1;
112 | LastSwiftMigration = 1100;
113 | };
114 | };
115 | };
116 | buildConfigurationList = AD2713DE22A94A70004A042C /* Build configuration list for PBXProject "AppKit Cocoa Resizable NSView" */;
117 | compatibilityVersion = "Xcode 9.3";
118 | developmentRegion = en;
119 | hasScannedForEncodings = 0;
120 | knownRegions = (
121 | en,
122 | Base,
123 | );
124 | mainGroup = AD2713DA22A94A70004A042C;
125 | packageReferences = (
126 | EFB48DA623E556ED00D1D769 /* XCRemoteSwiftPackageReference "AutoLayoutProxy" */,
127 | );
128 | productRefGroup = AD2713E422A94A70004A042C /* Products */;
129 | projectDirPath = "";
130 | projectRoot = "";
131 | targets = (
132 | AD2713E222A94A70004A042C /* AppKit Cocoa Resizable NSView */,
133 | );
134 | };
135 | /* End PBXProject section */
136 |
137 | /* Begin PBXResourcesBuildPhase section */
138 | AD2713E122A94A70004A042C /* Resources */ = {
139 | isa = PBXResourcesBuildPhase;
140 | buildActionMask = 2147483647;
141 | files = (
142 | AD2713E922A94A72004A042C /* Assets.xcassets in Resources */,
143 | );
144 | runOnlyForDeploymentPostprocessing = 0;
145 | };
146 | /* End PBXResourcesBuildPhase section */
147 |
148 | /* Begin PBXSourcesBuildPhase section */
149 | AD2713DF22A94A70004A042C /* Sources */ = {
150 | isa = PBXSourcesBuildPhase;
151 | buildActionMask = 2147483647;
152 | files = (
153 | AD2713FF22A94B11004A042C /* ViewController.swift in Sources */,
154 | AD2713FD22A94AEF004A042C /* ResizableView.swift in Sources */,
155 | AD2713FA22A94AB0004A042C /* AppDelegate.swift in Sources */,
156 | AD2713FB22A94AB0004A042C /* NSViewHelpers.swift in Sources */,
157 | AD27145822AF69C7004A042C /* DraggableResizableView.swift in Sources */,
158 | AD2713F922A94AB0004A042C /* main.swift in Sources */,
159 | );
160 | runOnlyForDeploymentPostprocessing = 0;
161 | };
162 | /* End PBXSourcesBuildPhase section */
163 |
164 | /* Begin XCBuildConfiguration section */
165 | AD2713EF22A94A72004A042C /* Debug */ = {
166 | isa = XCBuildConfiguration;
167 | buildSettings = {
168 | ALWAYS_SEARCH_USER_PATHS = NO;
169 | CLANG_ANALYZER_NONNULL = YES;
170 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
171 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
172 | CLANG_CXX_LIBRARY = "libc++";
173 | CLANG_ENABLE_MODULES = YES;
174 | CLANG_ENABLE_OBJC_ARC = YES;
175 | CLANG_ENABLE_OBJC_WEAK = YES;
176 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
177 | CLANG_WARN_BOOL_CONVERSION = YES;
178 | CLANG_WARN_COMMA = YES;
179 | CLANG_WARN_CONSTANT_CONVERSION = YES;
180 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
181 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
182 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
183 | CLANG_WARN_EMPTY_BODY = YES;
184 | CLANG_WARN_ENUM_CONVERSION = YES;
185 | CLANG_WARN_INFINITE_RECURSION = YES;
186 | CLANG_WARN_INT_CONVERSION = YES;
187 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
188 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
189 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
190 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
191 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
192 | CLANG_WARN_STRICT_PROTOTYPES = YES;
193 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
194 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
195 | CLANG_WARN_UNREACHABLE_CODE = YES;
196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
197 | CODE_SIGN_IDENTITY = "-";
198 | COPY_PHASE_STRIP = NO;
199 | DEBUG_INFORMATION_FORMAT = dwarf;
200 | ENABLE_STRICT_OBJC_MSGSEND = YES;
201 | ENABLE_TESTABILITY = YES;
202 | GCC_C_LANGUAGE_STANDARD = gnu11;
203 | GCC_DYNAMIC_NO_PIC = NO;
204 | GCC_NO_COMMON_BLOCKS = YES;
205 | GCC_OPTIMIZATION_LEVEL = 0;
206 | GCC_PREPROCESSOR_DEFINITIONS = (
207 | "DEBUG=1",
208 | "$(inherited)",
209 | );
210 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
211 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
212 | GCC_WARN_UNDECLARED_SELECTOR = YES;
213 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
214 | GCC_WARN_UNUSED_FUNCTION = YES;
215 | GCC_WARN_UNUSED_VARIABLE = YES;
216 | MACOSX_DEPLOYMENT_TARGET = 10.14;
217 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
218 | MTL_FAST_MATH = YES;
219 | ONLY_ACTIVE_ARCH = YES;
220 | SDKROOT = macosx;
221 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
222 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
223 | };
224 | name = Debug;
225 | };
226 | AD2713F022A94A72004A042C /* Release */ = {
227 | isa = XCBuildConfiguration;
228 | buildSettings = {
229 | ALWAYS_SEARCH_USER_PATHS = NO;
230 | CLANG_ANALYZER_NONNULL = YES;
231 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
232 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
233 | CLANG_CXX_LIBRARY = "libc++";
234 | CLANG_ENABLE_MODULES = YES;
235 | CLANG_ENABLE_OBJC_ARC = YES;
236 | CLANG_ENABLE_OBJC_WEAK = YES;
237 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
238 | CLANG_WARN_BOOL_CONVERSION = YES;
239 | CLANG_WARN_COMMA = YES;
240 | CLANG_WARN_CONSTANT_CONVERSION = YES;
241 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
242 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
243 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
244 | CLANG_WARN_EMPTY_BODY = YES;
245 | CLANG_WARN_ENUM_CONVERSION = YES;
246 | CLANG_WARN_INFINITE_RECURSION = YES;
247 | CLANG_WARN_INT_CONVERSION = YES;
248 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
249 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
250 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
251 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
252 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
253 | CLANG_WARN_STRICT_PROTOTYPES = YES;
254 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
255 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
256 | CLANG_WARN_UNREACHABLE_CODE = YES;
257 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
258 | CODE_SIGN_IDENTITY = "-";
259 | COPY_PHASE_STRIP = NO;
260 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
261 | ENABLE_NS_ASSERTIONS = NO;
262 | ENABLE_STRICT_OBJC_MSGSEND = YES;
263 | GCC_C_LANGUAGE_STANDARD = gnu11;
264 | GCC_NO_COMMON_BLOCKS = YES;
265 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
266 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
267 | GCC_WARN_UNDECLARED_SELECTOR = YES;
268 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
269 | GCC_WARN_UNUSED_FUNCTION = YES;
270 | GCC_WARN_UNUSED_VARIABLE = YES;
271 | MACOSX_DEPLOYMENT_TARGET = 10.14;
272 | MTL_ENABLE_DEBUG_INFO = NO;
273 | MTL_FAST_MATH = YES;
274 | SDKROOT = macosx;
275 | SWIFT_COMPILATION_MODE = wholemodule;
276 | SWIFT_OPTIMIZATION_LEVEL = "-O";
277 | };
278 | name = Release;
279 | };
280 | AD2713F222A94A72004A042C /* Debug */ = {
281 | isa = XCBuildConfiguration;
282 | buildSettings = {
283 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
284 | CLANG_ENABLE_MODULES = YES;
285 | CODE_SIGN_ENTITLEMENTS = "AppKit Cocoa Resizable NSView/AppKit_Cocoa_Resizable_NSView.entitlements";
286 | CODE_SIGN_IDENTITY = "-";
287 | CODE_SIGN_STYLE = Automatic;
288 | COMBINE_HIDPI_IMAGES = YES;
289 | INFOPLIST_FILE = "AppKit Cocoa Resizable NSView/Info.plist";
290 | LD_RUNPATH_SEARCH_PATHS = (
291 | "$(inherited)",
292 | "@executable_path/../Frameworks",
293 | );
294 | PRODUCT_BUNDLE_IDENTIFIER = "com.pulickan.AppKit-Cocoa-Resizable-NSView";
295 | PRODUCT_NAME = "$(TARGET_NAME)";
296 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
297 | SWIFT_VERSION = 5.0;
298 | };
299 | name = Debug;
300 | };
301 | AD2713F322A94A72004A042C /* Release */ = {
302 | isa = XCBuildConfiguration;
303 | buildSettings = {
304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
305 | CLANG_ENABLE_MODULES = YES;
306 | CODE_SIGN_ENTITLEMENTS = "AppKit Cocoa Resizable NSView/AppKit_Cocoa_Resizable_NSView.entitlements";
307 | CODE_SIGN_IDENTITY = "-";
308 | CODE_SIGN_STYLE = Automatic;
309 | COMBINE_HIDPI_IMAGES = YES;
310 | INFOPLIST_FILE = "AppKit Cocoa Resizable NSView/Info.plist";
311 | LD_RUNPATH_SEARCH_PATHS = (
312 | "$(inherited)",
313 | "@executable_path/../Frameworks",
314 | );
315 | PRODUCT_BUNDLE_IDENTIFIER = "com.pulickan.AppKit-Cocoa-Resizable-NSView";
316 | PRODUCT_NAME = "$(TARGET_NAME)";
317 | SWIFT_VERSION = 5.0;
318 | };
319 | name = Release;
320 | };
321 | /* End XCBuildConfiguration section */
322 |
323 | /* Begin XCConfigurationList section */
324 | AD2713DE22A94A70004A042C /* Build configuration list for PBXProject "AppKit Cocoa Resizable NSView" */ = {
325 | isa = XCConfigurationList;
326 | buildConfigurations = (
327 | AD2713EF22A94A72004A042C /* Debug */,
328 | AD2713F022A94A72004A042C /* Release */,
329 | );
330 | defaultConfigurationIsVisible = 0;
331 | defaultConfigurationName = Release;
332 | };
333 | AD2713F122A94A72004A042C /* Build configuration list for PBXNativeTarget "AppKit Cocoa Resizable NSView" */ = {
334 | isa = XCConfigurationList;
335 | buildConfigurations = (
336 | AD2713F222A94A72004A042C /* Debug */,
337 | AD2713F322A94A72004A042C /* Release */,
338 | );
339 | defaultConfigurationIsVisible = 0;
340 | defaultConfigurationName = Release;
341 | };
342 | /* End XCConfigurationList section */
343 |
344 | /* Begin XCRemoteSwiftPackageReference section */
345 | EFB48DA623E556ED00D1D769 /* XCRemoteSwiftPackageReference "AutoLayoutProxy" */ = {
346 | isa = XCRemoteSwiftPackageReference;
347 | repositoryURL = "https://github.com/bibinjacobpulickal/AutoLayoutProxy";
348 | requirement = {
349 | kind = upToNextMajorVersion;
350 | minimumVersion = 2.4.2;
351 | };
352 | };
353 | /* End XCRemoteSwiftPackageReference section */
354 |
355 | /* Begin XCSwiftPackageProductDependency section */
356 | EFB48DA723E556ED00D1D769 /* AutoLayoutProxy */ = {
357 | isa = XCSwiftPackageProductDependency;
358 | package = EFB48DA623E556ED00D1D769 /* XCRemoteSwiftPackageReference "AutoLayoutProxy" */;
359 | productName = AutoLayoutProxy;
360 | };
361 | /* End XCSwiftPackageProductDependency section */
362 | };
363 | rootObject = AD2713DB22A94A70004A042C /* Project object */;
364 | }
365 |
--------------------------------------------------------------------------------