MacOS: Custom NSView won't show subviews - swift

I have a View Controller and added a Subview to its View. The class of the Subview should also have some Subviews etc. Here is my code of my custom Class:
import Foundation
import Cocoa
class PercentageShower: NSView {
let percentageBar = NSView()
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.wantsLayer = true
self.layer?.backgroundColor = CGColor.black
let testsubview = NSView(frame: frameRect)
testsubview.wantsLayer = true
testsubview.layer?.backgroundColor = CGColor.init(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
self.addSubview(testsubview)
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
}
}
This is the ViewControllers code:
class Basics1ViewController: NSViewController {
let percentageShower = PercentageShower()
override func viewDidLoad() {
super.viewDidLoad()
// Set screen mesurements:
let screen = NSScreen.main
let screenWidth = (screen?.frame.width)!
let screenHeight = (screen?.frame.height)!
// Set Percentage View
percentageShower.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
self.view.addSubview(percentageShower)
}
}
This is just an example, but why isn't my red colored subview shown? I can see the black colored view itself but not it's subview. Looking forward to your help!

Related

NSSlider custom subclass - how to maintain the link between the knob position and user interaction?

Trying to create a custom NSSlider. Overriding the drawKnob() method of the NSSliderCell changes the knob's appearance but doing this somehow disconnects the link between the knob's position and user interactions with the slider.
In the objective C example that is often referenced (https://github.com/lucasderraugh/LADSlider) it looks like when you override drawKnob() you then need to explicitly deal with the startTracking method, but I haven't found a solution that works for me - at the moment I am just setting the cell's startTracking 'at' property to the current value of the slider, but not sure what the right approach is.
Quite a few examples include an NSCoder argument in the cell's custom initialiser - I don't understand why, but maybe this has something to do with ensuring a connection between the display of the knob and the actual slider value?
import Cocoa
class ViewController: NSViewController {
var seekSlider = NSSlider()
var seekSliderCell = SeekSliderCell()
override func viewDidLoad() {
super.viewDidLoad()
seekSlider = NSSlider(target: self, action: #selector(self.seek(_:)))
seekSlider.cell = seekSliderCell
seekSlider.cell?.target = self
seekSlider.cell?.action = #selector(self.seek(_:))
seekSlider.isEnabled = true
seekSlider.isContinuous = true
view.addSubview(seekSlider)
}
#objc func seek(_ sender: NSObject) {
let val = seekSlider.cell?.floatValue
let point = NSPoint(x: Double(val!), y: 0.0)
seekSlider.cell?.startTracking(at: point, in: self.seekSlider)
}
}
class SeekSliderCell: NSSliderCell {
// required init(coder aDecoder: NSCoder) {
// super.init(coder: aDecoder)
// }
override func drawKnob() {
let frame = NSRect(x: 0.0, y: 6.0, width: 20.0, height: 10.0)
let c = NSColor(red: 0.9, green: 0.0, blue: 0.6, alpha: 1.0)
c.setFill()
NSBezierPath.init(roundedRect: frame, xRadius: 3, yRadius: 3).fill()
}
override func startTracking(at startPoint: NSPoint, in controlView: NSView) -> Bool {
return true
}
}
The documentation of drawKnob() states:
Special Considerations
If you create a subclass of NSSliderCell, don’t override this method. Override drawKnob(_:) instead.
Instead of
func drawKnob()
override
func drawKnob(_ knobRect: NSRect)
Example:
class ViewController: NSViewController {
var seekSlider = NSSlider()
var seekSliderCell = SeekSliderCell()
override func viewDidLoad() {
super.viewDidLoad()
seekSlider = NSSlider(target: self, action: #selector(self.seek(_:)))
seekSlider.cell = seekSliderCell
seekSlider.cell?.target = self
seekSlider.cell?.action = #selector(self.seek(_:))
seekSlider.isEnabled = true
seekSlider.isContinuous = true
view.addSubview(seekSlider)
}
#objc func seek(_ sender: NSObject) {
let val = seekSlider.cell?.floatValue
print("\(String(describing: val))")
}
}
class SeekSliderCell: NSSliderCell {
override func drawKnob(_ knobRect: NSRect) {
var frame = NSRect(x: 0.0, y: 6.0, width: 20.0, height: 10.0)
frame.origin.x = knobRect.origin.x + (knobRect.size.width - frame.size.width) / 2
frame.origin.y = knobRect.origin.y + (knobRect.size.height - frame.size.height) / 2
let c = NSColor(red: 0.9, green: 0.0, blue: 0.6, alpha: 1.0)
c.setFill()
NSBezierPath.init(roundedRect: frame, xRadius: 3, yRadius: 3).fill()
}
}

Swift - get random CGPoints from UIView

I am trying to get random CGPoints along the 'outer contour' of UIView so that I will be able to draw UIBezierPath line over obtained CGPoints. For example, get CGPoints from below red dots which I marked myself by hand. Any idea?
###Edit###
As per Sweeper's advice I am trying to add CAShapeLayer into my custom view but it returns nil when I am printing its path. Please could you point out what I am missing?
class ViewController: UIViewController {
let shapeLayer = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
let view = BubbleView(frame: CGRect(x: self.view.frame.midX / 2, y: self.view.frame.midY/2, width: 150, height: 100))
view.backgroundColor = .gray
view.layer.cornerRadius = 30
self.view.addSubview(view)
}
}
class BubbleView: UIView {
var pathLayer: CAShapeLayer!
override func layoutSubviews() {
pathLayer = CAShapeLayer()
pathLayer.bounds = frame
self.layer.addSublayer(pathLayer)
print(pathLayer.path) // returns nil
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Adding custom UIView to the view controller causes it to disappear in the storyboard

I created a custom UIView and then added it to a number of viewControllers. The view controller become glitchy and do not render in the storyboard. I just see the view controllers' outlines and no content. If I remove the custom view then all goes back to normal.
Can someone please take a look at my custom view code and see if I am doing anything wrong? Thank you so much.
import UIKit
#IBDesignable
class ProgressView: UIView {
#IBOutlet var contentView: UIView!
#IBInspectable var percentageProgress: CGFloat = 0.0 {
didSet {
self.setGradient()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("ProgressView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
setGradient()
}
private func setGradient() {
if let sublayers = self.contentView.layer.sublayers {
for sublayer in sublayers {
sublayer.removeFromSuperlayer()
}
}
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor(#colorLiteral(red: 0.3935321569, green: 0.3986310661, blue: 0.6819975972, alpha: 1)).cgColor, UIColor(#colorLiteral(red: 0.1705667078, green: 0.649338007, blue: 0.5616513491, alpha: 1)).cgColor]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
gradientLayer.cornerRadius = contentView.frame.height / 2
contentView.layer.cornerRadius = contentView.frame.height / 2
self.contentView.layer.insertSublayer(gradientLayer, at: 0)
gradientLayer.frame = CGRect(x: contentView.frame.minX, y: contentView.frame.minY,
width: self.contentView.frame.maxX * (percentageProgress ?? 1.0), height:
contentView.frame.maxY)
self.contentView.setNeedsDisplay()
}
}

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

NSScrollView subclass doesn't scroll

I'm making a macOS app and i have created a simple NSScrollView subclass. However, the scrollview doesn't scroll.
When i set the property NSScrollView.hasHorizontalScroller or the property NSScrollView.hasVerticalScroller to true,
then the scrolling works. But i want the scrollbars to be hidden, so these properties will be set to false.
This is my swift (playground) code:
import Cocoa
import PlaygroundSupport
class MyScrollView: NSScrollView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
// fatalError("init(coder:) has not been implemented")
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
}
let scrollView = MyScrollView(frame: NSRect(x: 0, y: 0, width: 480, height: 240))
// scrollView.hasHorizontalScroller = true
// Image of 1280 x 854.
let img = NSImage(byReferencing: URL(fileURLWithPath: "/users/username/Pictures/some-image.jpg"))
let imgView = NSImageView(frame: NSRect(x: 0, y: 0, width: img.size.width, height: img.size.height))
imgView.image = img
scrollView.documentView = imgView
PlaygroundPage.current.liveView = scrollView
I have compared many properties with a normal NSScrollView, but i can't figure it out.
What am i doing wrong?
Thanks in advance.