CGColor and UIDeviceRGBColor leaks - swift

I have a weird problem - the Xcode issue navigator reports that my app leaks CGColor and UIDeviceRGBColor.
The class in which this occurs seems fairly simple to me, but hoping someone can point me in the right direction.
import Foundation
internal enum TriangleDirection {
case up
case down
}
class TriangleView: UIView {
let size: CGFloat = 12.0
var direction: TriangleDirection = .up
var colour = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 0.7)
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
myInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
myInit()
}
func myInit() {
backgroundColor = UIColor.clear
}
// change to up or down
func set(direction: TriangleDirection, colour: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 0.7)) {
self.direction = direction
self.colour = colour
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
// general declarations
let context = UIGraphicsGetCurrentContext()
let origin = CGPoint(x: 0, y: 0)
// draw triangle
context!.saveGState()
context!.translateBy(x: origin.x, y: origin.y)
let trianglePath = UIBezierPath()
// draw depending on direction...
if direction == .up {
trianglePath.move(to: CGPoint(x: size/2, y: 0))
trianglePath.addLine(to: CGPoint(x: size, y: (size * 0.8)))
trianglePath.addLine(to: CGPoint(x: 0.0, y: (size * 0.8)))
} else {
trianglePath.move(to: CGPoint(x: size/2, y: (size * 0.8)))
trianglePath.addLine(to: CGPoint(x: 0, y: 0))
trianglePath.addLine(to: CGPoint(x: size, y: 0))
}
trianglePath.close()
colour.setFill()
trianglePath.fill()
context!.restoreGState()
}
override var intrinsicContentSize: CGSize {
return CGSize(width: size, height: size)
}
}
Issue Navigator warnings

Related

make the uibutton rounded, shadow and gradient

can someone tell me please how to make the button rounded, shadow and gradient
here I set the gradient and shadow to the button, but without rounding:
#IBOutlet weak var info: UIButton!
info.setTitle("INFO", for: .normal)
info.setTwoGradients(colorOne: Colors.OrangeGrad, colorTwo: Colors.OrangeGradSec)
info.setTitleColor(UIColor.black, for: .normal)
info.layer.shadowColor = UIColor.darkGray.cgColor
info.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
info.layer.shadowOpacity = 0.8
info.layer.shadowRadius = 2
view.addSubview(info)
I know that the shadow disappears because of the rounding and I found a way to fix it, ex:
final class CustomButton: UIButton {
private var shadowLayer: CAShapeLayer!
override func layoutSubviews() {
super.layoutSubviews()
if shadowLayer == nil {
shadowLayer = CAShapeLayer()
shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
shadowLayer.fillColor = UIColor.white.cgColor
shadowLayer.shadowColor = UIColor.darkGray.cgColor
shadowLayer.shadowPath = shadowLayer.path
shadowLayer.shadowOffset = CGSize(width: 5.0, height: 2.0)
shadowLayer.shadowOpacity = 0.7
shadowLayer.shadowRadius = 2
layer.insertSublayer(shadowLayer, at: 0)
}
}
but there is a line:
shadowLayer.fillColor = UIColor.white.cgColor
because of which I can't set the gradient
therefore, I cannot find a way by which all three conditions would be met
Output:
Usage
class ViewController: UIViewController {
#IBOutlet var btnGradient: CustomButton!
override func viewDidLoad() {
super.viewDidLoad()
btnGradient.gradientColors = [.red, .green]
btnGradient.setTitle("Gradient Button", for: .normal)
btnGradient.setTitleColor(.white, for: .normal)
}
}
Custom Class
Use this class as a reference to setup the attributes:
class CustomButton: UIButton {
var gradientColors : [UIColor] = [] {
didSet {
setupView()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
let startPoint = CGPoint(x: 0, y: 0.5)
let endPoint = CGPoint(x: 1, y: 0.5)
var btnConfig = UIButton.Configuration.plain()
btnConfig.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: layer.frame.height / 2, bottom: 5, trailing: layer.frame.height / 2)
self.configuration = btnConfig
layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
//Gradient
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = gradientColors.map { $0.cgColor }
gradientLayer.startPoint = startPoint
gradientLayer.endPoint = endPoint
gradientLayer.cornerRadius = layer.frame.height / 2
if let oldValue = layer.sublayers?[0] as? CAGradientLayer {
layer.replaceSublayer(oldValue, with: gradientLayer)
} else {
layer.insertSublayer(gradientLayer, below: nil)
}
//Shadow
layer.shadowColor = UIColor.darkGray.cgColor
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: layer.frame.height / 2).cgPath
layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
layer.shadowOpacity = 0.7
layer.shadowRadius = 2.0
}
}
You can use UIButton Extension or refer code
Pass colors in array with start & end point of gradient effect, you want to start & end. i.e. x=0, y=0 means TopLeft & x=1, y=1 means BottomRight
extension UIButton {
func setGradientLayer(colorsInOrder colors: [CGColor], startPoint sPoint: CGPoint = CGPoint(x: 0, y: 0.5), endPoint ePoint: CGPoint = CGPoint(x: 1, y: 0.5)) {
let gLayer = CAGradientLayer()
gLayer.frame = self.bounds
gLayer.colors = colors
gLayer.startPoint = sPoint
gLayer.endPoint = ePoint
gLayer.cornerRadius = 5
gLayer.shadowOpacity = 0.8
gLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
layer.insertSublayer(gLayer, at: 0)
}
}

custom UIButton class - gradient going out off button borders

I'm doing custom UIButton class and I want it to have gradient background. My gradient color is looking perfect but it goes out of button borders
My code:
class CustomButton: UIButton{
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup(){
layer.cornerRadius = 25.0
layer.borderWidth = 1
gradientLayer.frame = layer.bounds
addShadow()
}
private lazy var gradientLayer: CAGradientLayer = {
let color1 = UIColor(red: 254.0/255.0, green: 79.0/255.0, blue: 50.0/255.0, alpha: 1.0).cgColor
let color2 = UIColor(red: 255.0/255.0, green: 26.0/255.0, blue: 107.0/255.0, alpha: 1.0).cgColor
let gradient = CAGradientLayer()
gradient.frame = layer.bounds
gradient.colors = [color1, color2]
gradient.startPoint = CGPoint(x: 0, y: 0.5)
gradient.endPoint = CGPoint(x: 1, y: 0.5)
gradient.locations = [0,1]
gradient.cornerRadius = 25
layer.insertSublayer(gradient, at: 0)
return gradient
}()
func addShadow(){
layer.shadowOpacity = 1
layer.shadowRadius = 1.0
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 1
layer.shadowOffset = CGSize(width: 0, height: 3)
}
}
And here's how it looks:
Remove gradientLayer.frame from setup and add in layoutSubviews.
Also set self.bounds instead of layer.bounds.
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = self.bounds // < Here
}
Note : Remove gradient.frame = layer.bounds from var gradientLayer. Not needed.
Just override layerClass to avoid resizing issue (Auto resize with constrains)
class CustomButton: UIButton{
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override class var layerClass: AnyClass {
CAGradientLayer.self
}
var gradientLayer: CAGradientLayer {
layer as! CAGradientLayer
}
func setup(){
gradientLayer.cornerRadius = 25.0
gradientLayer.borderWidth = 1
let color1 = UIColor(red: 254.0/255.0, green: 79.0/255.0, blue: 50.0/255.0, alpha: 1.0).cgColor
let color2 = UIColor(red: 255.0/255.0, green: 26.0/255.0, blue: 107.0/255.0, alpha: 1.0).cgColor
gradientLayer.colors = [color1, color2]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
gradientLayer.locations = [0,1]
gradientLayer.shadowOpacity = 1
gradientLayer.shadowRadius = 1.0
gradientLayer.shadowColor = UIColor.black.cgColor
gradientLayer.shadowOpacity = 1
gradientLayer.shadowOffset = CGSize(width: 0, height: 3)
}
}

My BeizerPath is above my labels. How can I make my labels appear above?

I'm looking to make my dopePointlabel appear above my beizerPath. I created a custom shape and I want my label to appear above it. I tried z-index and that does not seem to work. In what way can I make my Beizer Path below my text so my text is viewable. Thank you in advance.
class DemoView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
var path: UIBezierPath!
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func createRectangle() {
path = UIBezierPath()
path.move(to: CGPoint(x:0.0, y: 0.0))
path.addLine(to: CGPoint(x: 0.0, y: 300.0))
//path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height))
//self.frame.size.height
path.addCurve(to:CGPoint(x: self.frame.size.width, y: 300.0), controlPoint1: CGPoint(x: self.frame.size.width/2, y: 400), controlPoint2: CGPoint(x: self.frame.size.width/2, y: 400))
path.addLine(to: CGPoint(x: self.frame.size.width, y: 0.0))
path.close()
}
override func draw(_ rect: CGRect) {
self.createRectangle()
UIColor.orange.setFill()
path.fill()
UIColor.purple.setStroke()
path.stroke()
}
}
view controller code below
let height: CGFloat = 400.0
let demoView = DemoView(frame: CGRect(x: 0,
y: self.view.frame.size.height/2 - height/2,
width: self.view.frame.size.width,
height: height))
dopepointsNumberLabel.layer.zPosition = 1
self.view.addSubview(demoView)
Change
self.view.addSubview(demoView)
to
self.view.insertSubview(demoView,at:0)

Why are UIViews background color is not updating?

Switch statement works but won't reset view background colours etc.
I have a UIImage (icon) and a UIButton embedded within a UIView (of custom type DropShadowCircleView) as per image below.
When the walking button is tapped a var navigationOption is set to either walking or driving and setupNavigationSelectionView() is executed.
Problem is: case "walking" of the switch works perfectly, but case "driving" doesn't reset the UIView and icon tint color witcback to their original setting eg; background color etc.. any ideas why?
func setupNavigationSelectionView(){
switch navigationOption {
case "walking":
walkingBg.setGradientBackground(colourOne: softGreen, ColourTwo: softBlue)
walkingBg.layer.cornerRadius = walkingBg.frame.width / 2
walkingBg.clipsToBounds = true
walkingIcon.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
case "driving":
walkingBg.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
walkingBg.layer.cornerRadius = walkingBg.frame.width / 2
walkingBg.clipsToBounds = true
walkingIcon.tintColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
default:
break
}
}
EDIT: this is my DropShadowCircleView class
class DropShadowCircleView: UIView {
override func awakeFromNib() {
setupView()
super.awakeFromNib()
}
func setupView(){
self.layer.shadowOpacity = 0.50
self.layer.shadowRadius = 20
self.layer.shadowColor = UIColor.black.cgColor
self.layer.cornerRadius = self.frame.width / 2
}
}
EDIT: This is my setGradientBackground function which is within an extension file to UIView
func setGradientBackground(colourOne: UIColor, ColourTwo: UIColor) {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [colourOne.cgColor, ColourTwo.cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.startPoint = CGPoint(x: 1.0, y: 1.0)
gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0)
layer.insertSublayer(gradientLayer, at: 0)
}
You need to remove your gradient layer when you reset your icon.
Add this to your extension UIView:
func removeGradientBackground() {
guard
let idx = layer.sublayers?.index(where: { $0 is CAGradientLayer })
else { return }
layer.sublayers?.remove(at: idx)
}
and call it when you are resetting your icon.

my drawRect function will not update

I want the values for the colors of the circle to update when the variables(redd1,greenn1,bluee1) are changed by the steppers in my ViewController. The original circle is drawn by drawRect.
var redd1 = 0.0;
var greenn1 = 0.0;
var bluee1 = 0.0;
override init(frame: CGRect)
{
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
override func drawRect(rect: CGRect)
{
let circle2 = UIView(frame: CGRect(x: -25.0, y: 10.0, width: 100.0, height:100.0))
circle2.layer.cornerRadius = 50.0
let startingColor2 = UIColor(red: (CGFloat(redd1))/255, green: (CGFloat (greenn1))/255, blue: (CGFloat(bluee1))/255, alpha: 1.0)
circle2.backgroundColor = startingColor2;
addSubview(circle2);
}
I tried creating a new function that draws a new circle on top of the old one. It is called by the stepper. The new function, updateColor(), receives the new value from the stepper. This partially works because it prints out the correct new values, but never draws the new circle.
func updateColor()
{
let circle = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
circle.layer.cornerRadius = 50.0;
let startingColor = UIColor(red: (CGFloat(redd1))/255, green: (CGFloat(greenn1))/255, blue: (CGFloat(bluee1))/255, alpha: 1.0)
circle.backgroundColor = startingColor;
addSubview(circle);
}
I'm not so good in Swift, but that's a simple way how i wood do it:
var circle2: UIView
override func drawRect(rect: CGRect) {
circle2 = UIView(frame: CGRect(x: -25.0, y: 10.0, width: 100.0, height:100.0))
circle2.layer.cornerRadius = 50.0
let startingColor2 = UIColor(red: (CGFloat(redd1))/255, green: (CGFloat (greenn1))/255, blue: (CGFloat(bluee1))/255, alpha: 1.0)
circle2.backgroundColor = startingColor2;
addSubview(circle2);
}
#IBAction valueChanged(sender: UIStepper) {
changeColor(color(the Color you want), Int(the Int value you want the color to change at))
}
func changeColor(color: UIColor, number: Int) {
if stepper.value == number {
circle2.backgroundColor = color
}
}
You have to create a IBAction for the stepper of the type value changed and call the method for every color and to it belonging Int in the IBAction for the stepper. So every time + or - on the stepper is pressed the IBAction calls the changeColor method and the changeColor method tests which color should be set to circle2.