Drawing a circle in Swift (MacOS) - swift

I've found this code for drawing the line for MacOS app.
class Drawing: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let context = NSGraphicsContext.current?.cgContext;
context!.beginPath()
context!.move(to: CGPoint(x: 0.0, y: 0.0))
context!.addLine(to: CGPoint(x: 100.0, y: 100.0))
context!.setStrokeColor(red: 1, green: 0, blue: 0, alpha: 1)
context!.setLineWidth(1.0)
context!.strokePath()
}
override func viewDidLoad() {
super.viewDidLoad()
let dr = Drawing(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
self.view.addSubview(dr)
}
How to change this code for circle? It's difficult for me to solve this problem. Help me, please.

The circle equivalent is
class Drawing: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let context = NSGraphicsContext.current!.cgContext
context.saveGState()
context.setFillColor(NSColor.red.cgColor)
context.fillEllipse(in: dirtyRect)
context.restoreGState()
}
}
or the classic NSBezierPath way
class Drawing: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let fillColor = NSColor.red
let path = NSBezierPath(ovalIn: dirtyRect)
fillColor.setFill()
path.fill()
}
}

Depending on the side of the circle, you can also do this:
class YourParentView: NSView {
// Create it as a view of its own
let circleView = NSView()
circleView.wantsLayer = true
circleView.layer?.cornerRadius = 7
//depending on the size, adjust this
//and that's it. Now it's a circle.
//Then just addict the style
circleView.layer?.backgroundColor = NSColor.green.cgColor
circleView.layer?.borderColor = NSColor.white.cgColor
//Be sure to add it to the parent view
self.view.addSubview(circleView)
}

Related

How to draw NSRect in a new location

I am a newbie to Swift coding
Below is a custom view class:
class MyView: NSView {
var xloc = 0.0
var yloc = 0.0
var width = 400.0
var height = 200.0
var myRect = NSRect(x: 0.0, y: 0.0, width: 400.0, height: 200.0)
...
override func draw(_ dirtyRect: NSRect) {
let myColor = NSColor(red: 1.0, green: 1.0, blue: 1.0, alpha: self.alpha)
myColor.setFill()
dirtyRect.fill()
self.myRect = NSRect(x: self.xloc, y: self.yloc, width: self.width, height: self.height)
self.setNeedsDisplay(myRect)
self.myRect.fill(using: NSCompositingOperation.clear)
}
The view controller has a keyDown handler as follows:
override func viewDidLoad() {
super.viewDidLoad()
view.window?.backgroundColor = NSColor.white
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
self.keyDown(with: $0)
return $0
}
}
override func keyDown(with event: NSEvent) {
let theView = self.view as! MyView
if event.keyCode == 126 {
NSLog("Increasing Y coordinate")
theView.yloc = theView.yloc + 10
self.view.display()
}
}
Below is the AppDelegate.swift's content:
func applicationDidFinishLaunching(_ aNotification: Notification) {
let window = NSApplication.shared.windows.first
window?.isOpaque = false
window?.backgroundColor = NSColor.clear
window?.ignoresMouseEvents = true
window?.toggleFullScreen(self)
window?.collectionBehavior = NSWindow.CollectionBehavior.fullScreenPrimary
let screenFrame = NSScreen.main?.frame
window?.setFrame(screenFrame!, display: true)
let myView = MyView()
window?.contentView = myView
}
The rectangular hole is seen on the screen initially
The above handler is called whenever I press the up arrow key. The self.view.display() in turn calls the draw() method on the view. But the NSRect is not drawn in the new position
Not sure what is wrong

Cocoa: Set NSView mask as CAShapeLayer

Hi I am trying to set CAShapeLayer inside to NSView. But I can't set corner radius of the CAShapeLayer outside of draw(_ dirtyRect: NSRect) function.
I have to use CAShapeLayer to set NSBezierPath to draw custom shapes. So, that only I am using CAShapeLayer instead of the NSView's default CALayer.
Here is the code:
extension NSBezierPath
extension NSBezierPath {
var cgPath: CGPath {
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< elementCount {
let type = element(at: i, associatedPoints: &points)
switch type {
case .moveTo: path.move(to: points[0])
case .lineTo: path.addLine(to: points[0])
case .curveTo: path.addCurve(to: points[2], control1: points[0], control2: points[1])
case .closePath: path.closeSubpath()
#unknown default:
fatalError("Unknown!")
}
}
return path
}
}
CustomView.swift
class CustomView: NSView{
let shapeLayer = CAShapeLayer()
var cornerRadius: CGFloat {
set{
let path = NSBezierPath(roundedRect: bounds, xRadius: newValue, yRadius: newValue)
shapeLayer.path = path.cgPath
path.close()
}
get{
return shapeLayer.cornerRadius
}
}
var backgroundColor: NSColor{
set{
shapeLayer.fillColor = newValue.cgColor
}
get{
return NSColor(cgColor: shapeLayer.backgroundColor ?? .clear)!
}
}
init() {
super.init(frame: .zero)
wantsLayer = true
shapeLayer.masksToBounds = true
layer?.addSublayer(shapeLayer)
// layer?.mask = shapeLayer
// layer?.masksToBounds = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ dirtyRect: NSRect) {
shapeLayer.frame = dirtyRect
let path = NSBezierPath(rect: dirtyRect)
shapeLayer.path = path.cgPath
// shapeLayer.cornerRadius = 22
shapeLayer.borderColor = NSColor.black.cgColor
shapeLayer.borderWidth = 2.0
path.close()
}
}
ViewController.swift
class ViewController: NSViewController {
private lazy var customView: CustomView = {
let customView = CustomView()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
customView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
customView.heightAnchor.constraint(equalToConstant: 44),
customView.widthAnchor.constraint(equalToConstant: 144)
])
return customView
}()
override func viewDidLoad() {
super.viewDidLoad()
// customView.layer?.cornerRadius = 22
customView.backgroundColor = .red
customView.cornerRadius = 22
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Current output:
Expected Output:
Code is commented in the attached codes..
Please help me to solve this problem. Thank you in advance...
The following is a simple example. First, draw a simple rectangle with a subclass of NSView. So instantiate it to make an object with a view controller. Then use object's layer to make it a round rect with a stroke color.
// Subclass of `NSView` //
import Cocoa
class MyView: NSView {
var fillColor: NSColor
init(frame: CGRect, fillColor: NSColor){
self.fillColor = fillColor
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: NSRect) {
super.draw(rect)
let path = NSBezierPath()
path.move(to: CGPoint.zero)
path.line(to: CGPoint(x: 0.0, y: self.frame.height))
path.line(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.line(to: CGPoint(x: self.frame.width, y: 0.0))
path.line(to: CGPoint.zero)
fillColor.set()
path.fill()
}
}
// View controller //
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let rect = CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 200, height: 50))
let myView = MyView(frame: rect, fillColor: NSColor.red)
myView.wantsLayer = true
if let myLayer = myView.layer {
myLayer.cornerRadius = 24.0
myLayer.borderWidth = 4.0
//myLayer.borderColor = NSColor.green.cgColor
}
view.addSubview(myView)
}
}

How to split the background color of UIView in Swift?

I want to differentiate the background color in my app screen horizontally.
I have tried this, it's going nowhere.
var halfView1 = backgroundView.frame.width/2
backgroundView.backgroundColor.halfView1 = UIColor.black
backgroundView is an outlet from View object on storyboard
For example, half of the screen is blue and another half of the screen is red.
You should create a custom UIView class and override draw rect method
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(HorizontalView(frame: self.view.bounds))
}
}
class HorizontalView: UIView {
override func draw(_ rect: CGRect) {
super.draw(rect)
let topRect = CGRect(x: 0, y: 0, width: rect.size.width/2, height: rect.size.height)
UIColor.red.set()
guard let topContext = UIGraphicsGetCurrentContext() else { return }
topContext.fill(topRect)
let bottomRect = CGRect(x: rect.size.width/2, y: 0, width: rect.size.width/2, height: rect.size.height)
UIColor.green.set()
guard let bottomContext = UIGraphicsGetCurrentContext() else { return }
bottomContext.fill(bottomRect)
}
}
It is possible if you use a custom UIView and override the draw function, here's a Playground example :
import UIKit
import PlaygroundSupport
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.green
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
super.draw(rect)
let bottomRect = CGRect(
origin: CGPoint(x: rect.origin.x, y: rect.height / 2),
size: CGSize(width: rect.size.width, height: rect.size.height / 2)
)
UIColor.red.set()
guard let context = UIGraphicsGetCurrentContext() else { return }
context.fill(bottomRect)
}
}
let view = CustomView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
PlaygroundPage.current.liveView = view

MacOS does not display NSBezierPath line

I'm trying to draw a line in my MacOS app but I do not see the line. What can be a problem?
My code looks like:
func addLine() {
let path = NSBezierPath()
path.move(to: NSPoint(x: 100.0, y: 100))
path.line(to: NSPoint(x: 200.0, y: 200.0))
NSColor.green.setFill()
NSColor.green.setStroke()
path.close()
path.stroke()
}
And I call it in:
override func viewDidLoad() {
super.viewDidLoad()
addLine()
}
Am I doing something wrong? I just do not see anything in my window.
Have you created your own subclass of a NSView?
If I create a new view and add your code like so:
import Cocoa
class MyView: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
addLine()
}
func addLine() {
let path = NSBezierPath()
path.move(to: NSPoint(x: 100.0, y: 100))
path.line(to: NSPoint(x: 200.0, y: 200.0))
NSColor.green.setFill()
NSColor.green.setStroke()
path.close()
path.stroke()
}
}
And I then - in a storyboard - drag a "Custom View" to the canvas, change the type of the view to be MyView like so
Then I see this when running the app:
If you prefer to add the view in code, you can do something like this:
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let myView = MyView()
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
myView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
myView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
myView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
}
So, your code seems to work, I'm just not sure how you are trying to use it.
Hope that gives you something to continue with.

Delete a filled NSBezierPath

I've drawn a red circle. Then I would like to delete it. How can I do that?
class Red: NSView {
var red = 255
var green = 0
var blue = 0
override func draw(_ dirtyRect: NSRect) {
let circleFillColor = NSColor(red: CGFloat(red), green: CGFloat(green), blue: CGFloat(blue), alpha: 1)
let cPath: NSBezierPath = NSBezierPath(ovalIn: dirtyRect)
circleFillColor.set()
cPath.fill()
}
}
override func viewDidLoad() {
super.viewDidLoad()
let signal = Red(frame: NSRect(x: 146, y: 18, width: 25, height: 25))
self.view.addSubview(signal)
}
You can use removeFromSuperview method of the view to get if removed, as such:
class ViewController: NSViewController {
var signal: Red?
override func viewDidLoad() {
super.viewDidLoad()
self.signal = Red(frame: NSRect(x: 146, y: 18, width: 25, height: 25))
self.view.addSubview(signal!)
}
func deleteCircle() {
self.signal?.removeFromSuperview()
self.signal = nil
}
}