Skip to content

Instantly share code, notes, and snippets.

@almonk
Last active September 24, 2024 18:46
Show Gist options
  • Save almonk/9ef97d3a8da74bdcb9874aba19fc0d98 to your computer and use it in GitHub Desktop.
Save almonk/9ef97d3a8da74bdcb9874aba19fc0d98 to your computer and use it in GitHub Desktop.
SnappyWindow – A NSWindow that acts like the PIP Video Window from Safari

To use;

  • Simply download SnappyWindow.swift from this gist and add it to your Xcode project
  • Instantiate a window with SnappyWindow()
import Cocoa
import SwiftUI
@main
class AppDelegate: NSObject, NSApplicationDelegate {
var window: SnappyWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
let contentView = ContentView()
// Create the window and set the content view.
window = SnappyWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
window.center()
window.isMovableByWindowBackground = true
window.contentView = NSHostingView(rootView: contentView)
window.makeKeyAndOrderFront(nil)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
//
// SnappyWindow.swift
// A NSWindow that snaps to corners
//
// Created by Alasdair Monk on 03/02/2021.
//
import Foundation
import Cocoa
/// An NSWindow with the super power to snap to corners of the screen
class SnappyWindow: NSWindow, NSWindowDelegate {
enum SnapPosition: Int {
case bottomLeft = 1
case bottomRight = 4
case topLeft = 2
case topRight = 3
}
/// Padding from edge of screen
let padding: CGFloat = 10
/// The latest position the window was snapped to
var snapPosition: SnapPosition?
private var hasBeenDragged: Bool = false
override func mouseDragged(with event: NSEvent) {
print("Drag")
self.hasBeenDragged = true
}
override func mouseUp(with event: NSEvent) {
print("Mouse up")
if self.hasBeenDragged {
let mouseY = NSEvent.mouseLocation.y
let mouseX = NSEvent.mouseLocation.x
if mouseY > (NSScreen.main?.frame.height)! / 2 && mouseX < (NSScreen.main?.frame.width)! / 2 {
self.snapTo(position: .topLeft)
self.snapPosition = .topLeft
}
if mouseY <= (NSScreen.main?.frame.height)! / 2 && mouseX < (NSScreen.main?.frame.width)! / 2 {
self.snapTo(position: .bottomLeft)
self.snapPosition = .bottomLeft
}
if mouseY > (NSScreen.main?.frame.height)! / 2 && mouseX >= (NSScreen.main?.frame.width)! / 2 {
self.snapTo(position: .topRight)
self.snapPosition = .topRight
}
if mouseY <= (NSScreen.main?.frame.height)! / 2 && mouseX >= (NSScreen.main?.frame.width)! / 2 {
self.snapTo(position: .bottomRight)
self.snapPosition = .bottomRight
}
self.hasBeenDragged = false
}
}
func snapTo(position: SnapPosition) {
switch position {
case .bottomLeft:
self.animator().setFrame(NSRect(x: padding, y: padding + padding, width: self.frame.width, height: self.frame.height), display: false, animate: true)
case .topLeft:
let position = (NSScreen.main?.frame.height)! - self.frame.height - padding
self.animator().setFrame(NSRect(x: padding, y: position, width: self.frame.width, height: self.frame.height), display: false, animate: true)
case .bottomRight:
self.animator().setFrame(NSRect(x: (NSScreen.main?.frame.width)! - self.frame.width - padding, y: padding + padding, width: self.frame.width, height: self.frame.height), display: false, animate: true)
case .topRight:
self.animator().setFrame(NSRect(x: (NSScreen.main?.frame.width)! - self.frame.width - padding, y: (NSScreen.main?.frame.height)! - self.frame.height - padding, width: self.frame.width, height: self.frame.height), display: false, animate: true)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment