Skip to content

Instantly share code, notes, and snippets.

@mayoff
Last active February 27, 2024 11:21
Show Gist options
  • Save mayoff/ea37ee75a87efab5d7e8 to your computer and use it in GitHub Desktop.
Save mayoff/ea37ee75a87efab5d7e8 to your computer and use it in GitHub Desktop.

Revisions

  1. mayoff renamed this gist Mar 6, 2017. 1 changed file with 0 additions and 0 deletions.
  2. mayoff created this gist Jan 17, 2016.
    118 changes: 118 additions & 0 deletions FloatiengViewController.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,118 @@
    import UIKit

    class FloatingButtonController: UIViewController {

    private(set) var button: UIButton!

    required init?(coder aDecoder: NSCoder) {
    fatalError()
    }

    init() {
    super.init(nibName: nil, bundle: nil)
    window.windowLevel = CGFloat.max
    window.hidden = false
    window.rootViewController = self

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardDidShow:", name: UIKeyboardDidShowNotification, object: nil)
    }

    private let window = FloatingButtonWindow()

    override func loadView() {
    let view = UIView()
    let button = UIButton(type: .Custom)
    button.setTitle("Floating", forState: .Normal)
    button.setTitleColor(UIColor.greenColor(), forState: .Normal)
    button.backgroundColor = UIColor.whiteColor()
    button.layer.shadowColor = UIColor.blackColor().CGColor
    button.layer.shadowRadius = 3
    button.layer.shadowOpacity = 0.8
    button.layer.shadowOffset = CGSize.zero
    button.sizeToFit()
    button.frame = CGRect(origin: CGPointMake(10, 10), size: button.bounds.size)
    button.autoresizingMask = []
    view.addSubview(button)
    self.view = view
    self.button = button
    window.button = button

    let panner = UIPanGestureRecognizer(target: self, action: "panDidFire:")
    button.addGestureRecognizer(panner)
    }

    override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    snapButtonToSocket()
    }

    func panDidFire(panner: UIPanGestureRecognizer) {
    let offset = panner.translationInView(view)
    panner.setTranslation(CGPoint.zero, inView: view)
    var center = button.center
    center.x += offset.x
    center.y += offset.y
    button.center = center

    if panner.state == .Ended || panner.state == .Cancelled {
    UIView.animateWithDuration(0.3) {
    self.snapButtonToSocket()
    }
    }
    }

    func keyboardDidShow(note: NSNotification) {
    window.windowLevel = 0
    window.windowLevel = CGFloat.max
    }

    private func snapButtonToSocket() {
    var bestSocket = CGPoint.zero
    var distanceToBestSocket = CGFloat.infinity
    let center = button.center
    for socket in sockets {
    let distance = hypot(center.x - socket.x, center.y - socket.y)
    if distance < distanceToBestSocket {
    distanceToBestSocket = distance
    bestSocket = socket
    }
    }
    button.center = bestSocket
    }

    private var sockets: [CGPoint] {
    let buttonSize = button.bounds.size
    let rect = view.bounds.insetBy(dx: 4 + buttonSize.width / 2, dy: 4 + buttonSize.height / 2)
    let sockets: [CGPoint] = [
    CGPointMake(rect.minX, rect.minY),
    CGPointMake(rect.minX, rect.maxY),
    CGPointMake(rect.maxX, rect.minY),
    CGPointMake(rect.maxX, rect.maxY),
    CGPointMake(rect.midX, rect.midY)
    ]
    return sockets
    }

    }

    private class FloatingButtonWindow: UIWindow {

    var button: UIButton?

    init() {
    super.init(frame: UIScreen.mainScreen().bounds)
    backgroundColor = nil
    }

    required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }


    private override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
    guard let button = button else { return false }
    let buttonPoint = convertPoint(point, toView: button)
    return button.pointInside(buttonPoint, withEvent: event)
    }

    }