How is adding these UIButton extension functions making my app perform better? - swift

After failing to get a live image filter on a Metal view to perform better, I was curious if my UI had some kind of impact. I have an extension on UIButton that adds a blur and shadow to them. If I comment out the stuff inside the functions, and run it, the GPU works twice as hard and the battery impact races beyond high in the debug navigator.
extension UIButton
{
func addCircularBlur(radius:CGFloat)
{
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
blur.frame = self.bounds
blur.layer.cornerRadius = radius * self.bounds.size.width
blur.clipsToBounds = true
blur.isUserInteractionEnabled = false
blur.autoresizingMask = [.flexibleRightMargin, .flexibleLeftMargin, .flexibleBottomMargin, .flexibleTopMargin, .flexibleWidth, .flexibleHeight]
self.insertSubview(blur, at: 0)
if let imageView = self.imageView{
self.bringSubview(toFront: imageView)
}
}
func addShadow()
{
self.imageView?.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1.0).cgColor
self.imageView?.layer.shadowOffset = CGSize(width:0.0, height:0.0)
self.imageView?.layer.shadowOpacity = 0.1
self.imageView?.layer.shadowRadius = 2.0
self.imageView?.layer.masksToBounds = false
self.imageView?.layer.cornerRadius = 4.0
}
}
Here's how I'm calling these functions. This example uses a button that is from Interface Builder, but it can be added to a button programmatically:
#IBOutlet weak var takePhotoBtn: UIButton!
// inside viewDidLoad:
takePhotoBtn.addCircularBlur(radius:0.5)
takePhotoBtn.addShadow()
What about this is making my device perform better when the blur and shadows are ON?

Related

textfield bottom line issue in swift

I am using the following code in Swift 4.2 to have Textfield bottom border:
extension UITextField {
func useUnderline() {
let border = CALayer()
let borderWidth = CGFloat(1.0)
border.borderColor = UIColor.lightGray.cgColor
border.frame = CGRect(origin: CGPoint(x: 0,y :self.frame.size.height - borderWidth), size: CGSize(width: self.frame.size.width, height: self.frame.size.height))
border.borderWidth = borderWidth
self.layer.addSublayer(border)
self.layer.masksToBounds = true
}
}
but in different model of iPhones I get a different behaviors. for example:
in Iphone XR:
and with iPhone X or 8:
Any solution to this issue would be appreciated.
I'm sure this problem does not depend on an iPhone model, but from the place where you call useUnderline(). If you will call useUnderline() from viewDidAppear, then the line will draw correct. Because the size of UIControls not yet actual when calling viewDidLoad and viewWillAppear.
But keep in your mind you will have a problem with your extension because sublayers will be added to UITextField on every viewDidAppear execution.
This could be a prime example where you must use a UITextField subclass instead of an extension. The accepted answer will work, but as suggested there, your TextField will get many layers added to it as the user uses his application.
The below subclass could solve this issue:
class CustomTextField: UITextField{
let border = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
useUnderline()
}
func useUnderline(){
if layer.sublayers?.contains(border) ?? false{
border.removeFromSuperlayer()
}
border.path = UIBezierPath.init(rect: CGRect.init(x: 0, y: self.bounds.height - 2, width: self.bounds.width, height: 2)).cgPath
border.fillColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.3).cgColor
self.layer.insertSublayer(border, at: 10)
}
}

Add glow effect to text of a UI button - swift

my question is quite straight forward. I am aiming to add a basic glow effect to a button in swift. I want the text to glow, not the entire button box.
I have attached an image as an example to illustrate what I am aiming to achieve.
I have looked elsewhere but typically only find animations which is not what I want. Sorry the image is of poor quality.
I am currently using this code but my settings button appears with a very weak glow, how can I make it stronger:
import UIKit
extension UIView {
enum GlowEffect: Float {
case small = 0.4, normal = 2, big = 30
}
func doGlowAnimation(withColor color: UIColor, withEffect effect: GlowEffect = .normal) {
layer.masksToBounds = false
layer.shadowColor = color.cgColor
layer.shadowRadius = 0
layer.shadowOpacity = 1
layer.shadowOffset = .zero
let glowAnimation = CABasicAnimation(keyPath: "shadowRadius")
glowAnimation.fromValue = 20 // effect.rawValue
glowAnimation.toValue = 20
glowAnimation.fillMode = .removed
glowAnimation.repeatCount = .infinity
layer.add(glowAnimation, forKey: "shadowGlowingAnimation")
}
}
Changing the intensity doesn't give that strong color effect near the individual letters
To create an outer glow effect on the title of a UIButton you'll want to make sure you adjust the shadow properties of the UIButton's titleLabel. Meaning you could run your animation by saying:
button.titleLabel?.doGlowAnimation(withColor: UIColor.yellow)
The animation adjusts shadowRadius though currently goes from 20 to 20 so there's no actual animation.
extension UIView {
enum GlowEffect: Float {
case small = 0.4, normal = 2, big = 30
}
func doGlowAnimation(withColor color: UIColor, withEffect effect: GlowEffect = .normal) {
layer.masksToBounds = false
layer.shadowColor = color.cgColor
layer.shadowRadius = 0
layer.shadowOpacity = 0.8
layer.shadowOffset = .zero
let glowAnimation = CABasicAnimation(keyPath: "shadowRadius")
glowAnimation.fromValue = 0
glowAnimation.toValue = effect.rawValue
glowAnimation.fillMode = .removed
glowAnimation.repeatCount = .infinity
glowAnimation.duration = 2
glowAnimation.autoreverses = true
layer.add(glowAnimation, forKey: "shadowGlowingAnimation")
}
}
Provides a pulsating outer glow, growing over the course of two seconds then reversing.

Swift 3 - Custom button (image and text) only draws correctly if there's no outlet?

EDIT: Although I accepted an answer that helped me get the buttons drawn correctly at runtime, I still have other issues. The root of which, I suspect, is the issue of why giving my custom button an outlet interferes with how it is drawn. I still need to know why this is. (see my answer below)
I have my own button class that extends UIButton (see below) and has several IBInspectable properties like border width/color, corner radius, and even start/end colors for a gradient background. I also use such properties for setting the image and titles' insets programmatically so I can account for various screen sizes.
Previously I had an issue where if I changed the "View as" device in the storyboard, let's say from iPhone SE to iPhone 7, and then refreshed the views and ran on a physical iPhone SE it would deploy with the insets of an iPhone 7 instead of calculating the insets with the physical device's own screen size. However, I nearly resolved this problem by overriding draw in my custom button class.
My problem now is that the overridden draw method is only called (or seems to be effective only when) the button has no outlet to the ViewController class it's placed in.
For example:
*The img is a placeholder; the insets suit the real imgs appropriately.
That bottom-right button has the gradient and corners drawn properly, where the other 3 do not. I have confirmed that adding an outlet to this button makes it behave like the rest, and removing an outlet from any of the other 3 causes it to be drawn correctly.
For reference, the outlets are just
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
immediately under the class MyViewController: UIViewController { declaration.
Also, I didn't forget to set the Custom Class in the interface builder for each button.
Here is the button class:
import UIKit
#IBDesignable
class ActionButton: UIButton {
override init(frame: CGRect){
super.init(frame: frame)
setupView()
}
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)!
setupView()
}
#IBInspectable var cornerRadius: CGFloat = 0{
didSet{
setupView()
}
}
#IBInspectable var startColor: UIColor = UIColor.white{
didSet{
setupView()
}
}
#IBInspectable var endColor: UIColor = UIColor.black{
didSet{
setupView()
}
}
#IBInspectable var borderWidth: CGFloat = 0{
didSet{
self.layer.borderWidth = borderWidth
}
}
#IBInspectable var borderColor: UIColor = UIColor.clear{
didSet{
self.layer.borderColor = borderColor.cgColor
}
}
private func setupView(){
let colors:Array = [startColor.cgColor, endColor.cgColor]
gradientLayer.colors = colors
gradientLayer.cornerRadius = cornerRadius
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
self.setNeedsDisplay()
}
var gradientLayer: CAGradientLayer{
return layer as! CAGradientLayer
}
override func draw(_ rect: CGRect) {
//Draw image
setupView()
let btnW = self.frame.size.width
let btnH = self.frame.size.height
//Compute and set image insets
let topBtnInset = btnH * (-5/81)
let bottomBtnInset = -4 * topBtnInset
let leftBtnInset = btnW / 4 //btnW * (35/141)
let rightBtnInset = leftBtnInset
self.imageEdgeInsets = UIEdgeInsets(top: topBtnInset, left: leftBtnInset, bottom: bottomBtnInset, right: rightBtnInset)
//Compute and set title insets
let topTitleInset = btnH * (47/81)
let bottomTitleInset = CGFloat(0)
let leftTitleInset = CGFloat(-256) //Image width
let rightTitleInset = CGFloat(0)
self.titleEdgeInsets = UIEdgeInsets(top: topTitleInset, left: leftTitleInset, bottom: bottomTitleInset, right: rightTitleInset)
//Draw text
//Default font size
self.titleLabel?.font = UIFont.boldSystemFont(ofSize: 15)
if(btnH > 81){
self.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
}else if(btnH > 97){
self.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
}
}
override class var layerClass: AnyClass{
return CAGradientLayer.self
}
}
Please ignore the wonky way I handled insets, font sizes, etc..
Does anyone know why specifying an outlet for the button makes it not draw the layer correctly?
A couple of thoughts:
The draw(_:) method is for stroking paths, drawing images, etc. It is for rendering the view at a given moment of time. You should not be updating layers, UIKit subviews, etc.
In your snippet, your draw(_:) is calling setupView, which then calls setNeedsDisplay (which theoretically could trigger draw(_:) to be called again). I'd suggest reserving draw(_:) for only those things that need to be manually drawn. (As it is now, you probably could completely eliminate this method as there's nothing here that is drawing anything.)
For any manual configuration of subviews (their frames, etc.), that should be moved to layoutSubviews, which is called whenever the OS determines that the subviews might need to have their frame values adjusted.
If you have any troubles with designable views, it's worth confirming that you have these in a separate target. It minimizes what needs to be recompiled when rendering in IB. It also prevents issues in the main target affecting the ability to render these designables in IB. (When Apple introduced designable views, they were very specific that one should put them in a separate target.)
I've also had intermittent problems with designable views in Xcode, but these problems are often resolved by quitting Xcode, emptying the derived data folder, and restarting.
I must confess that I do not know why adding an outlet to a designable view would affect how the view is rendered. I know that when I did the above, though, I was unable to reproduce your problem.
Anyway, I tweaked your ActionButton like so, and it appears to work for me:
#import UIKit
#IBDesignable
public class ActionButton: UIButton {
public override init(frame: CGRect = .zero) {
super.init(frame: frame)
setupView()
}
public required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setupView()
}
#IBInspectable public var cornerRadius: CGFloat = 0 { didSet { setupView() } }
#IBInspectable public var startColor: UIColor = .white { didSet { setupView() } }
#IBInspectable public var endColor: UIColor = .black { didSet { setupView() } }
#IBInspectable public var borderWidth: CGFloat = 0 { didSet { layer.borderWidth = borderWidth } }
#IBInspectable public var borderColor: UIColor = .clear { didSet { layer.borderColor = borderColor.cgColor } }
private func setupView() {
let colors = [startColor.cgColor, endColor.cgColor]
gradientLayer.colors = colors
gradientLayer.cornerRadius = cornerRadius
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
}
private var gradientLayer: CAGradientLayer {
return layer as! CAGradientLayer
}
public override func layoutSubviews() {
super.layoutSubviews()
let btnW = frame.width
let btnH = frame.height
//Compute and set image insets
let topBtnInset = btnH * (-5/81)
let bottomBtnInset = -4 * topBtnInset
let leftBtnInset = btnW / 4 //btnW * (35/141)
let rightBtnInset = leftBtnInset
imageEdgeInsets = UIEdgeInsets(top: topBtnInset, left: leftBtnInset, bottom: bottomBtnInset, right: rightBtnInset)
//Compute and set title insets
let topTitleInset = btnH * (47/81)
let bottomTitleInset = CGFloat(0)
let leftTitleInset = CGFloat(-256) //THIS MUST BE EQUAL TO -IMAGE WIDTH
let rightTitleInset = CGFloat(0)
titleEdgeInsets = UIEdgeInsets(top: topTitleInset, left: leftTitleInset, bottom: bottomTitleInset, right: rightTitleInset)
//Adjust text font
if btnH > 81 {
titleLabel?.font = .boldSystemFont(ofSize: 17)
} else if btnH > 97 {
titleLabel?.font = .boldSystemFont(ofSize: 20)
} else {
//Default font size
titleLabel?.font = .boldSystemFont(ofSize: 15)
}
}
public override class var layerClass: AnyClass {
return CAGradientLayer.self
}
}
I've accepted Rob's answer because it is far more likely to be of use to anyone else, but I've found what specifically was my issue, on the off-chance someone has a similar problem.
Apparently in my code for the View Controller on which those buttons were placed I had a function I had forgot about that enables/disables those custom buttons, as well as set their background color (to a solid color). This is why those buttons looked wrong. The 4th button was never subject to mutation by this function. It was not the fact the other 3 had an outlet, but that when I removed the outlet I commented-out the call to that function.
Nevertheless, other problems were addressed in this question!
-I had a really sloppy implementation of a UIButton that overrode and called UI functions improperly, that caused problems like freezing on rotation. These were also addressed in Rob's (the accepted) answer.
-"Refresh All Views" in the Interface Builder was hanging on "Signing product", which was caused by calling self.titleLabel?.font. According to some answers on the Apple developer forums, doing this in layoutSubViews() is bad!

Swift Scroll View with slider and MIDI

Im using Scroll View to scroll a picture. I'm using a slider 1 - 26 to retrieve CGPoint from an Array for x axis points. Everything behaves as expected and the image is smooth in transition.
I also set up a button that when pressed count from 1 - 26 and loops to retrieve CGPoint from an Array for x axis points. Everything behaves as expected and the image is smooth in transition.
I have also set up MIDI that sends data 1 - 26 based on velocity this is sent to the slider, and the slider moves as expected. I also using MIDI to retrieve CGPoint from an Array for x axis points. Unfortunately although it wants to scroll the following pictures shows what happens you only get to see 4 images as it scrolls and you can tell by the moving slider when they appear
I'm very confident that the Midi is working correctly as i use it all the time to read from arrays. I have also tried triggering the button from Midi a kind of virtual press. This works as expected but shows the same as triggering directly from midi. It seems as though everything is fine as long as the scroll originates from a IB component.
Under Midi control the scroll bar also stutters but if i set
clipView.wantsLayer = true
The picture does not scroll under midi but the scroll bar moves as expected. Even though the slider and the button work as expected.
I have also tried all the things commented out in the playMidi. As well trying all the different parameters in the IB section.
I am at a point where i really could do with some help please.
Here is my code which hopefully also explains what i have tried.
I have also tried various
needsDisplay = true
on all views
Xcode 8 Swift3 OSX not iOS
Thank you for you time
import Cocoa
import CoreMIDI
class MainWindowController: NSWindowController {
static var subWindowController: MainWindowController!
var pointArray:[Double] = [99.00,0.00,40.00,80.00,120.00,160.00,200.00,240.00,280.00,320.00,360.00,400.00,440.00,480.00,520.00,560.00,600.00,640.00,680.00,720.00,760.00,800.00,840.00,880.00,920.00,960.00,1000.00]
#IBOutlet weak var scrollView: NSScrollView!
#IBOutlet weak var clipView: NSClipView!
#IBOutlet weak var ImageView: NSImageView!
#IBOutlet var scrollerBar: NSScroller!
#IBOutlet weak var sliderControl: NSSlider!
var myPointer = CGPoint.init(x: 0.0, y: 0.0)
var fromArray:Double = 0.0
var buttonCount = 0
override func windowDidLoad() {
super.windowDidLoad()
MainWindowController.subWindowController = self // USED for midi
//Initialize midi client
var midiClient: MIDIClientRef = 0;
var inPort:MIDIPortRef = 0;
var src:MIDIEndpointRef = MIDIGetSource(0);
MIDIClientCreate("MidiTestClient" as CFString, nil, nil, &midiClient);
MIDIInputPortCreate(midiClient, "MidiTest_InPort" as CFString, MyMIDIReadProc, nil, &inPort);
MIDIPortConnectSource(inPort, src, &src);
//Set paramameters
scrollerBar.doubleValue = 0.9
let scrollViewSize = NSSize(width: 287, height: 119)
scrollView.setFrameSize(scrollViewSize)
let scrollViewColor = CGColor(red: 0.0, green: 0.0, blue: 0.9, alpha: 0.5)
scrollView.wantsLayer = false
scrollView.layer?.backgroundColor = scrollViewColor
scrollView.drawsBackground = false
let clipViewSize = NSSize(width: 1274, height: 88)
clipView.setFrameSize(clipViewSize)
let clipViewColor = CGColor(red: 0.0, green: 0.0, blue: 0.9, alpha: 0.5)
clipView.wantsLayer = false //If true image freezes but scoller moves with midi
clipView.layer?.backgroundColor = clipViewColor
clipView.drawsBackground = false
let ImageViewSize = NSSize(width: 1274, height: 87)
ImageView.setFrameSize(ImageViewSize)
/* let ImageViewColor = CGColor(red: 0.0, green: 0.9, blue: 0.0, alpha: 0.5)
ImageView.wantsLayer = false
ImageView.layer?.backgroundColor = ImageViewColor*/
}//EO Overide
func playMidi(count:Int){
//print("MIDi",count)
let fromCountIndex = count // Gets 1 to 26 from midi
//Gets double from Array 1 to 26 on Index == 0.0 to 960.0
fromArray = pointArray[fromCountIndex]
/*SCROLL BAR THINGS I HAVE TRIED*/
let scrollerValue = fromArray/960
scrollerBar.doubleValue = scrollerValue
/* scrollerBar.display()
scrollerBar.isContinuous = true */
/*CLIP VIEW SCROLL FROM ARRAY TRIGGERED BY MIDI */
myPointer = CGPoint(x:fromArray,y:0.0)
clipView.scroll(myPointer)
/*scrollView.scroll(clipView, to: myPointer)*/
/*THIS FAKES A BUTTON FIRE BUT STILL SAME PROBLEM*/
/* let mySelector = #selector(myButton(_:))
myButton(mySelector as AnyObject)*/
/* DISPLAY VIEWS I HAVE TRIED*/
// scrollView.scrollsDynamically = true
/*MOVES THE SLIDER USING MIDI*/
sliderControl.integerValue = count
}//eo playMidi
#IBAction func myButton(_ sender: AnyObject) {
fromArray = pointArray[buttonCount]
myPointer = CGPoint(x:fromArray,y:0.0)
clipView.scroll(myPointer)
buttonCount = buttonCount + 1
if (buttonCount > 26){buttonCount = 0}
print("buttonCount",buttonCount)
}
#IBAction func mySlider(_ sender: AnyObject) {
let fromSlider = sender.integerValue * 1
//Gets double from Array 1 to 26 on Index == 0.0 to 960.0 on array set to 40 when using slider moves scroll bar
fromArray = pointArray[fromSlider]
myPointer = CGPoint(x:fromArray,y:0.0)
clipView.scroll(myPointer)
print("fromSlider",fromSlider)
}
}//EnD oF thE wORld
Further to my code as above I have not answered the question but instead found a "get around". I'm really not proud of this but it seems to pose a question that maybe someone might help in answering. As the code above using the Scroll View was not working i have instead tried using CAScrollLayer.
The basic setup is as follow. I have a view inside that view I created a Scroll Layer and inside that a layer. Again as above if I use a timer to fire the scroll view everything works perfectly. And again as above if i use Midi then the scroll doesn't happen. So the work around is. The midi 1-26 reads the point value the but instead of trying to send it to the scroll it send it to a transistion variable.
I have a "Run Button" on the interface this would normally work the scrollLayerScroll. It still does this but at an incredible speed. But instead of generation its own transition variable it uses the one created buy the midi function. There is a piece of code that makes sure only a unique number is sent to the point.
This works but really deserves a prize for the worst bit of coding ever.
I think there is something in the way the timer refreshes everything that i need to extract. I hoping someone may still take pity and help
thanks again
here is the revised code Xcode 8 Swift3 OSX not iOS
import Cocoa
import CoreMIDI
class MainWindowController: NSWindowController {
static var subWindowController: MainWindowController!
var myLayer = CALayer()
var myPointer = CGPoint.init(x: 0.0, y: 0.0)
var fromArray:Double = 0.0
var buttonCount = 0
var pointArray:[Double] = [99.00,0.00,40.00,80.00,120.00,160.00,200.00,240.00,280.00,320.00,360.00,400.00,440.00,480.00,520.00,560.00,600.00,640.00,680.00,720.00,760.00,800.00,840.00,880.00,920.00,960.00,1000.00]
#IBOutlet weak var myCustomView: NSView!
var translation: CGFloat = 0.0
var newTranslation:CGFloat = 0.0
var comp6:CGFloat = 0.0
var comp6a:CGFloat = 0.0
var scrollLayer : CAScrollLayer = {
let scrollLayer = CAScrollLayer() // 8
scrollLayer.bounds = CGRect(x: 0.0, y: 0.0, width: 150.0, height: 200.0) // 9
scrollLayer.position = CGPoint(x: 300/2, y: 180/2) // 10 //not sure about this
scrollLayer.borderColor = CGColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.8)
scrollLayer.borderWidth = 5.0 // 12
scrollLayer.scrollMode = kCAScrollHorizontally // 13
return scrollLayer
}()
var scrollTimer = Timer()
var layer = CALayer()
override func windowDidLoad() {
super.windowDidLoad()
MainWindowController.subWindowController = self // USED for midi
//Initialize midi client
var midiClient: MIDIClientRef = 0;
var inPort:MIDIPortRef = 0;
var src:MIDIEndpointRef = MIDIGetSource(0);
MIDIClientCreate("MidiTestClient" as CFString, nil, nil, &midiClient);
MIDIInputPortCreate(midiClient, "MidiTest_InPort" as CFString, MyMIDIReadProc, nil, &inPort);
MIDIPortConnectSource(inPort, src, &src);
//Set up layer
let img = NSImage(named: "smallSky.png")
let imageSize = img?.size
print (imageSize)
let layer = CALayer()
layer.bounds = CGRect(x: 0.0, y: 0.0, width: (imageSize?.width)!, height: (imageSize?.height)!) // 3
layer.position = CGPoint(x: (imageSize?.width)!/4, y: (imageSize?.height)!/2) // 4
layer.contents = img//5
myCustomView.layer?.addSublayer(scrollLayer)
scrollLayer.addSublayer(layer)
myCustomView.layer = scrollLayer
myCustomView.wantsLayer = true
}//EO Overide
#IBAction func runButton(_ sender: AnyObject) {
print("run")
//Sets of timer at very high Speed
scrollTimer = Timer.scheduledTimer(timeInterval: 0.0001, target: self, selector: #selector(MainWindowController.scrollLayerScroll as (MainWindowController) -> () -> ()), userInfo: nil, repeats: true)
}
func scrollLayerScroll(){//FIRED START FROM RUN BUTTON
//Collects data from translation and only sends to newTranslation when number is different
comp6a = translation
if (comp6a != comp6){
newTranslation = translation
print("newTranslation",newTranslation)
let newPoint = CGPoint(x: newTranslation,y:0.0)
scrollLayer.scroll(to: newPoint)
}
comp6 = comp6a
}//eo scrollLayerScroll
func playMidi(count:Int){
let fromCountIndex = count // Gets 1 to 26 from midi
fromArray = pointArray[fromCountIndex]
translation = CGFloat(fromArray)
}//eo playMidi
}//EnD oF thE wORld

Converting a CAShapeLayer to use in an NSImageView

I'm trying to port some iOS code for a Mac app. My code is as follows:
func innerRing() {
let innerRing = CAShapeLayer()
let circleRadius: CGFloat = 105.0
innerRing.frame = InnerRingView.bounds
func circleFrame() -> CGRect {
var circleFrame = CGRect(x: 0, y: 0, width: 2*circleRadius, height: 2*circleRadius)
circleFrame.origin.x = CGRectGetMidX(InnerRingView.bounds) - CGRectGetMidX(circleFrame)
circleFrame.origin.y = CGRectGetMidY(InnerRingView.bounds) - CGRectGetMidY(circleFrame)
return circleFrame
}
innerRing.path = UIBezierPath(ovalInRect: circleFrame()).CGPath
innerRing.lineWidth = 3.0
innerRing.strokeStart = 0.0
innerRing.strokeEnd = 1.0
innerRing.fillColor = UIColor.clearColor().CGColor
innerRing.strokeColor = UIColor(red: 147.0/255.0, green: 184.0/255.0, blue: 255.0/255.0, alpha: 1.0).CGColor
InnerRingView.layer.addSublayer(innerRing)
}
This code works very well, especially for adjusting the fill color, stroke color, and stroke start/end.
In my Mac app, I am effectively trying to use the same code, but apply it to an NSImageView (I want it to be able to appear on each row of a table, and I will adjust certain parameters (such as color) based on what that row details.
Could anyone assist with guidance on adding this simple circle to an NSImageView?
Why do you want to use an NSImageView? NSImageView is for displaying images (icons, pictures, etc).
Make yourself a custom NSView instead. Just remember that, unlike UIKit's UIView, NSView doesn't get a layer by default, so you need to tell It to by setting wantsLayer to true.
Like so:
class CircleView: NSView {
lazy var innerRing: CAShapeLayer = {
let innerRing = CAShapeLayer()
let circleRadius: CGFloat = 105.0
innerRing.frame = self.bounds
var circleFrame = CGRect(x: 0, y: 0, width: circleRadius, height: circleRadius)
circleFrame.origin.x = CGRectGetMidX(self.bounds) - CGRectGetMidX(circleFrame)
circleFrame.origin.y = CGRectGetMidY(self.bounds) - CGRectGetMidY(circleFrame)
innerRing.path = CGPathCreateWithEllipseInRect(circleFrame, nil)
innerRing.lineWidth = 3.0
innerRing.strokeStart = 0.0
innerRing.strokeEnd = 1.0
innerRing.fillColor = NSColor.clearColor().CGColor
innerRing.strokeColor = NSColor(red: 147.0/255.0, green: 184.0/255.0, blue: 255.0/255.0, alpha: 1.0).CGColor
return innerRing
}()
override func awakeFromNib() {
super.awakeFromNib()
wantsLayer = true
layer = CALayer()
layer?.addSublayer(innerRing)
}
}