Instantly share code, notes, and snippets.
Created
June 2, 2020 23:28
-
Star
1
(1)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save bubudrc/fd2785080420e5c118a0cec0d49d9616 to your computer and use it in GitHub Desktop.
macOS - NSButton. Custom view to works like a circular button, but full customizer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MWPCircularButton.swift | |
// StoreVisualizer | |
// | |
// Created by Perretta, Marcelo on 5/19/20. | |
// Copyright © 2020 MAWAPE Mobile. All rights reserved. | |
// | |
import Cocoa | |
import AppKit | |
protocol MWPCircularButtonDelegate: AnyObject { | |
func userDidClickedLeftButton(sender: MWPCircularButton) | |
} | |
@IBDesignable | |
open class MWPCircularButton: NSView { | |
@IBInspectable | |
public var backgroundColor: NSColor = .white{ | |
didSet { | |
self.needsDisplay = true | |
} | |
} | |
@IBInspectable | |
public var highlightBackgroundColor: NSColor = .blue { | |
didSet { | |
self.needsDisplay = true | |
} | |
} | |
@IBInspectable | |
public var titleButton: String = String.warning { | |
didSet { | |
self.needsDisplay = true | |
} | |
} | |
@IBInspectable | |
public var fontSize: CGFloat = 24.0 | |
@IBInspectable | |
public var titleYPosition: CGFloat = 7.0 | |
@IBInspectable | |
public var titleColor: NSColor = .white | |
@IBInspectable | |
public var isEnabled: Bool = true | |
@IBInspectable | |
public var borderWidth: CGFloat = 0 | |
@IBInspectable | |
public var borderColor: NSColor = .clear | |
weak var circularButtonDelegate: MWPCircularButtonDelegate? | |
override open func draw(_ dirtyRect: NSRect) { | |
super.draw(dirtyRect) | |
// Drawing code here. | |
let path = NSBezierPath(ovalIn: dirtyRect) | |
backgroundColor.setFill() | |
path.fill() | |
if borderWidth > 0 { | |
let borderRect = CGRect(x: 1.2, y: 1.2, width: self.bounds.size.width-borderWidth, height: self.bounds.size.height-borderWidth) | |
let border = CAShapeLayer() | |
border.frame = borderRect | |
border.lineWidth = borderWidth | |
border.path = NSBezierPath(ovalIn: borderRect).CGPath | |
border.strokeColor = borderColor.cgColor | |
border.fillColor = NSColor.clear.cgColor | |
self.layer?.addSublayer(border) | |
} | |
let textRect = NSRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height) | |
let textTextContent = titleButton | |
let textStyle = NSMutableParagraphStyle() | |
textStyle.alignment = .center | |
let textFontAttributes = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: fontSize), NSAttributedString.Key.foregroundColor: titleColor, NSAttributedString.Key.paragraphStyle: textStyle] | |
let textTextHeight: CGFloat = textTextContent.boundingRect(with: NSSize(width: textRect.width, height: CGFloat.infinity), options: NSString.DrawingOptions.usesLineFragmentOrigin, attributes: textFontAttributes).height | |
let textTextRect: NSRect = NSRect(x: 0, y:titleYPosition, width: textRect.width, height: textTextHeight) | |
NSGraphicsContext.saveGraphicsState() | |
textRect.clip() | |
textTextContent.draw(in: textTextRect.offsetBy(dx: 0, dy: 0), withAttributes: textFontAttributes) | |
NSGraphicsContext.restoreGraphicsState() | |
} | |
override open func mouseDown(with event: NSEvent) { | |
//TODO: highlight color / font | |
} | |
override open func mouseUp(with event: NSEvent) { | |
if isEnabled { | |
circularButtonDelegate?.userDidClickedLeftButton(sender: self) | |
} | |
} | |
} | |
public extension NSBezierPath { | |
var CGPath: CGPath { | |
let path = CGMutablePath() | |
var points = [CGPoint](repeating: .zero, count: 3) | |
for i in 0 ..< self.elementCount { | |
let type = self.element(at: i, associatedPoints: &points) | |
switch type { | |
case .moveTo: | |
path.move(to: CGPoint(x: points[0].x, y: points[0].y) ) | |
case .lineTo: | |
path.addLine(to: CGPoint(x: points[0].x, y: points[0].y) ) | |
case .curveTo: | |
path.addCurve( to: CGPoint(x: points[2].x, y: points[2].y), | |
control1: CGPoint(x: points[0].x, y: points[0].y), | |
control2: CGPoint(x: points[1].x, y: points[1].y) ) | |
case .closePath: | |
path.closeSubpath() | |
@unknown default: | |
break | |
} | |
} | |
return path | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment