Custom UILabel class does not change properties? - swift

I have a custom UILabel class. I create one CustomLabel and positioned it but it doesn't have properties that I gave in the custom UILabel class.
class CustomLabel: UILabel {
override func awakeFromNib() {
backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
textAlignment = .center
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
and here is my ViewController
class Controller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let lblPos = CGRect(x: view.frame.size.width / 2 - 75 , y: 100, width: 150 , height: 30)
let samsungLbl = CustomLabel(frame: lblPos)
samsungLbl.text = "Samsung"
view.addSubview(samsungLbl)
}
}

The problem is that you set properties at awakeFromNib method. From Apple Documentation
awakeFromNib()
The nib-loading infrastructure sends an awakeFromNib message to each object recreated from a nib archive
So this method is called when your object is loaded from a .xib file or a storyboad. Since you creating your object programmatically by calling init(frame: CGRect), your awaktFromNib is never called.
You need to move your setup code into another method and call from the initializer.
func setupStyle() {
backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
textAlignment = .center
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setupStyle()
}

Related

Swift shadow does not spread correctly

I'm been trying to create a shadow for my UIView. I looked around and found an extension for the CALayer class from this post. https://stackoverflow.com/a/48489506/9188318
So far its been working well for me until I try to put in a non 0 number for the spread.
With the spread being 0. This is the result
And here is the result using a spread of 1
And it gets even worse with a spread of 5
The problem that I'm having is that it doesn't have rounded corners and I have no idea how to fix this. Heres my code for the UIView that uses this view. The extension that is used to make the shadow is in the post above.
UIView code
class FileCalculateOperatorSelectionButton : UIView {
private var isActivated : Bool = false
//Background colors
private var unSelectedBackgroundColor : UIColor = UIColor(red: 178/255, green: 90/255, blue: 253/255, alpha: 1.0)
private var selectedBackgroundColor : UIColor = UIColor.white
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.commonInit()
}
private func commonInit() {
self.layer.cornerRadius = self.frame.height/2
self.backgroundColor = self.unSelectedBackgroundColor
let shadowColor = UIColor(red: 0xC8, green: 0xC6, blue: 0xC6)
//Change the spread argument here
self.layer.applySketchShadow(color: .black, alpha: 0.5, x: 0, y: 0, blur: 5, spread: 0)
}
}
Try setting the layer's masksToBounds property to true.
self.layer.masksToBounds = true
Change shadowPath in applySketchShadow function in CALayer extension like below;
// shadowPath = UIBezierPath(rect: rect).cgPath
shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
when spread: 1

Shadow at top side of UIView only

How can I remove shadow from bottom side of my view?
This is code for my shadowView:
class ShadowViewWhite: UIView {
init() {
super.init(frame: .zero)
backgroundColor = .white
layer.shadowColor = UIColor(red: 0.5, green: 0.5, blue: 0.65, alpha: 0.9).cgColor
layer.shadowOpacity = 1
layer.shadowRadius = 40
layer.shadowOffset = CGSize(width: 0, height: -12)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
That's how it looks so far. I need shadow to be only at the top side of my view.
A combination of reducing the shadowRadius and shadowOffset should help you. I have attached my code below which should help you.
class ShadowViewWhite: UIView {
override init(frame:CGRect) {
super.init(frame: frame)
backgroundColor = .white
layer.shadowColor = UIColor(red: 0.5, green: 0.5, blue: 0.65, alpha: 0.9).cgColor
layer.shadowOpacity = 1
layer.shadowRadius = 20
layer.shadowOffset = CGSize(width: 0, height: -40)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
This code will produce the following image:
Change shadowRadius property. Ex:
layer.shadowRadius = 24
Using lower values of shadow radius can help. You can try using shadowRadius = 20.

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()
}
}

Custom UILabel with gradient text colour always become black

I created a custom UILabel to show grediant text but it always show the text as black...when i but the same code on a view controller it works!!
import UIKit
class GradientLabel: UILabel {
override func awakeFromNib() {
super.awakeFromNib()
let gredient = GradientView.init(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
gredient.bottomColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
gredient.topColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)
setTextColorToGradient(image: imageWithView(view: gredient)!)
}
func imageWithView(view: UIView) -> UIImage? {//bet7awel uiview to image
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
func setTextColorToGradient(image: UIImage) {//beta7'od image we tekteb beha el text fe el label
UIGraphicsBeginImageContext(frame.size)
image.draw(in: bounds)
let myGradient = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
textColor = UIColor(patternImage: myGradient!)
}
}
You don’t want to rely on awakeFromNib. Furthermore, you really don’t want to do this in init, either, as you want to be able to respond to size changes (e.g. if you have constraints and the frame changes after the label is first created).
Instead, update your gradient from layoutSubviews, which is called whenever the view’s frame changes:
#IBDesignable
class GradientLabel: UILabel {
#IBInspectable var topColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1) {
didSet { setNeedsLayout() }
}
#IBInspectable var bottomColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1) {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
updateTextColor()
}
private func updateTextColor() {
let image = UIGraphicsImageRenderer(bounds: bounds).image { _ in
let gradient = GradientView(frame: bounds)
gradient.topColor = topColor
gradient.bottomColor = bottomColor
gradient.drawHierarchy(in: bounds, afterScreenUpdates: true)
}
textColor = UIColor(patternImage: image)
}
}
That results in:
Note,
I’ve used the new UIGraphicsImageRenderer rather than UIGraphicsBeginImageContext, UIGraphicsGetImageFromCurrentImageContext, and UIGraphicsEndImageContext.
Rather than building a CGRect manually, I just use bounds.
I’ve made it #IBDesignable so that I can use it directly in Interface Builder. I’ve also made the colors #IBInspectable so you can adjust the colors right in IB rather than going to code. Clearly, you only have to do this if you want to see the gradient effect rendered in IB.
I’ve made it update the gradient (a) when the label needs to be laid out again; and (b) whenever you change either of the colors.
For what it’s worth, this is the GradientView I used for the purposes of this example:
#IBDesignable
class GradientView: UIView {
override class var layerClass: AnyClass { return CAGradientLayer.self }
private var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer }
#IBInspectable var topColor: UIColor = .white { didSet { updateColors() } }
#IBInspectable var bottomColor: UIColor = .blue { didSet { updateColors() } }
override init(frame: CGRect = .zero) {
super.init(frame: frame)
updateColors()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateColors()
}
private func updateColors() {
gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor]
}
}
In this case, because we’re setting the layerClass to the gradient, all we need to do is to configure it during init, and the base layerClass will take care of responding the size changes.
Alternatively, you can just draw your gradient using CoreGraphics:
#IBDesignable
class GradientLabel: UILabel {
#IBInspectable var topColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1) {
didSet { setNeedsLayout() }
}
#IBInspectable var bottomColor: UIColor = #colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1) {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
updateTextColor()
}
private func updateTextColor() {
let image = UIGraphicsImageRenderer(bounds: bounds).image { context in
let colors = [topColor.cgColor, bottomColor.cgColor]
guard let gradient = CGGradient(colorsSpace: nil, colors: colors as CFArray, locations: nil) else { return }
context.cgContext.drawLinearGradient(gradient,
start: CGPoint(x: bounds.midX, y: bounds.minY),
end: CGPoint(x: bounds.midX, y: bounds.maxY),
options: [])
}
textColor = UIColor(patternImage: image)
}
}
This achieves the same as the first example, but is potentially a tad more efficient.
You should init your label instead of awakeFromNib cause you are not using a xib for your label
class GradientLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
and an extension would be perfect to make your code reusable
extension UILabel {
func setTextColorToGradient(_ image: UIImage) {
UIGraphicsBeginImageContext(frame.size)
image.draw(in: bounds)
let myGradient = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
textColor = UIColor(patternImage: myGradient!)
}
}
usage:
label.setTextColorToGradient(UIImage(named:"someImage")!)

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.