UIView in UITableViewCell Has Double Border - swift

With the code below, I add a colored border and round the corners passed. However, when I resize the UIView programmatically, the old border remains and I get this double border:
Anyone have an idea how to fix it? I've tried calling cell.layoutSubviews() and cell.layer.layoutSublayers(). I tried removing the border layer and setting the layers to nil before setting the border. Nothing seems to work.
if I don't resize the view, it's fine.
func roundCorners (view: UIView, corners: UIRectCorner, radius: CGFloat, color: String) {
let bounds = view.bounds
let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let maskLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.path = maskPath.cgPath
view.layer.mask = maskLayer
var borderColor = UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0)
switch color {
case "Green":
borderColor = UIColor(red: 130/255.0, green: 208/255.0, blue: 151/255.0, alpha: 0.9)
case "Red":
borderColor = UIColor(red: 243/255.0, green: 44/255.0, blue: 31/255.0, alpha: 1.0)
view.alpha = 0.5
case "Purp":
borderColor = UIColor(red: 102/255.0, green: 135/255.0, blue: 241/255.0, alpha: 0.7)
case "DarkBlue":
borderColor = UIColor(red: 90/255.0, green: 125/255.0, blue: 160/255.0, alpha: 0.6)
default:
borderColor = UIColor(red: 150/255.0, green: 150/255.0, blue: 150/255.0, alpha: 0.7)
}
let frameLayer = CAShapeLayer()
frameLayer.frame = bounds
frameLayer.path = maskPath.cgPath
frameLayer.strokeColor = borderColor.cgColor
frameLayer.lineWidth = 3
frameLayer.fillColor = nil
view.layer.addSublayer(frameLayer)
}
Called from cellForRowAt:
roundCorners(view: cell.MoveCellTo_TodayList, corners: [.topLeft, .bottomLeft], radius: 8, color: "Green")

Move maskLayer and frameLayer out of the function's scope and call the roundCorners inside the cell's overridden layoutSubviews.
var maskLayer: CAShapeLayer!
var frameLayer: CAShapeLayer!
override func layoutSubviews() {
super.layoutSubviews()
roundCorners(view: MoveCellTo_TodayList, maskLayer: &maskLayer, frameLayer: &frameLayer, corners: [.topLeft, .bottomLeft], radius: 8, color: "Green")
}
func roundCorners(view: UIView, maskLayer: inout CAShapeLayer, frameLayer: inout CAShapeLayer, corners: UIRectCorner, radius: CGFloat, color: String) {
let bounds = view.bounds
let maskPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
if maskLayer == nil {
maskLayer = CAShapeLayer()
view.layer.mask = maskLayer
frameLayer = CAShapeLayer()
view.layer.addSublayer(frameLayer)
}
[...]
}

Related

How to fix: gradient background at landscaping?

I have this code which creates a gradient background.
When I rotate the deviace to landscape mode, the gradient background doesnt fill up to the whole size of the screen, some parts are white.
My goal is the whenever the orrientation changes, the gradient should fill the whole screen.
let gradient: CAGradientLayer = CAGradientLayer()
let color1 = UIColor(red: 198.0/255.0 , green: 255.0/255.0, blue: 221.0/255.0, alpha: 1.0).cgColor
let color2 = UIColor(red: 251.0/255.0, green: 215.0/255.0, blue: 134.0/255.0, alpha: 1.0).cgColor
let color3 = UIColor(red: 247.0/255.0, green: 121.0/255.0, blue: 125.0/255.0, alpha: 1.0).cgColor
gradient.name="asd"
gradient.colors = [color1,color2,color3]
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
gradient.frame = CGRect(x: 0.0, y: 0.0, width: wid, height: heu)
self.view.layer.insertSublayer(gradient, at: 0)
}
Hold the gradient layer as a property and update its frame in layoutSubview()
var gradientLayer: CAGradientLayer?
override func viewDidLoad() {
super.viewDidLoad()
///
/// YOUR CODE
///
gradientLayer = gradient
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer?.frame = self.view.bounds
}

CGRect not appearing with gradient sublayer

CGRect not appearing at the same time as gradient sublayer, it's one or the other
Drawing only the gradient layer or only the rectangle works fine.
guard let ctx = UIGraphicsGetCurrentContext() else { fatalError() }
ctx.setFillColor(red: 1, green: 0.5, blue: 0.5, alpha: 1)
let segment1 = CGRect(x: 20, y: 110, width: 30, height: 10)
ctx.fill(segment1)
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = [UIColor.init(displayP3Red: 0.29, green: 0.56, blue: 1.0, alpha: 1).cgColor, UIColor.init(displayP3Red: 0.23, green: 0.19, blue: 0.8, alpha: 1).cgColor]
layer.insertSublayer(gradient, at: 0)
I was expecting them both to appear in the correct order
Use: gradient.render(in: ctx) instead of layer.insertSublayer(gradient, at: 0 and draw segement1 after that: (Assuming you are doing this in a custom view class)
class customView: UIView {
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { fatalError() }
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = [UIColor.init(displayP3Red: 0.29, green: 0.56, blue: 1.0, alpha: 1).cgColor, UIColor.init(displayP3Red: 0.23, green: 0.19, blue: 0.8, alpha: 1).cgColor]
gradient.render(in: ctx)
ctx.setFillColor(red: 1, green: 0.5, blue: 0.5, alpha: 1)
let segment1 = CGRect(x: 20, y: 110, width: 30, height: 10)
ctx.fill(segment1)
}
}
This way you aren't using the CGGraphicsContext in addition to the CALayer, you are just using the CGGraphicsContext because you layer gets rendered using the ctx

Why is my Gradient Layer smaller than the UITextField I am applying it to?

I am trying to give my text fields a gradient background. I am using the following code:
import UIKit
extension UITextField {
func gradientBackground(firstColor: UIColor, secondColor: UIColor){
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [firstColor.cgColor, secondColor.cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.startPoint = CGPoint(x:0.0, y:0.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
layer.addSublayer(gradientLayer)
}
}
class gradientTeLabel: UITextField {
override func didMoveToWindow() {
self.gradientBackground(firstColor: UIColor(red: 0.90, green: 0.61, blue: 0.00, alpha: 1), secondColor: UIColor(red: 0.70, green: 0.61, blue: 0.70, alpha: 1))
}
}
What I did was to create an extension and a class which I will attach to some UITextFields. The result is the gradient layer is a little bit shorter than the textfield and it kind of starts a few pixels higher inside the texfield instead of covering it all. What can I do?
Move code to layoutSubviews , As it contains the right bounds
class gradientTeLabel: UITextField {
var once = true
override func layoutSubviews() {
super.layoutSubviews()
if once {
self.gradientBackground(firstColor: UIColor(red: 0.90, green: 0.61, blue: 0.00, alpha: 1), secondColor: UIColor(red: 0.70, green: 0.61, blue: 0.70, alpha: 1))
once = false
}
}
}

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.

How to change the color of the Mask View?

I am not sure if I understand the concept of Masking correctly but I am trying to recreate the Twitter logo expansion animation in their app:
Twitter Logo expansion
I have this code so far:
class LaunchScreenViewController: UIViewController {
var mask: CALayer!
override func viewDidLoad() {
setUpViewMask()
}
func setUpViewMask() {
mask = CALayer()
mask.contents = UIImage(named: "logo_mask")?.cgImage
mask.contentsGravity = kCAGravityResizeAspect
mask!.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
mask!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
mask!.position = CGPoint(x: view.frame.size.width/2, y: view.frame.size.height/2)
view.layer.backgroundColor = UIColor(red: 70/255, green: 154/255, blue: 233/255, alpha: 1).cgColor
view.layer.mask = mask
}
}
The output of this is:
How would I change the black background to be blue? I tried doing but it didn't seem to work:
view.layer.backgroundColor = UIColor(red: 70/255, green: 154/255, blue: 233/255, alpha: 1).cgColor
Create an intermediate layer where you apply mask. Set the background of your view to the desired background, and set background color of the intermediate layer to the color that you wish your mask to appear in. Something like this,
view.backgroundColor = UIColor(red: 70/255, green: 154/255, blue: 233/255, alpha: 1) // set background of the view portion that do not include mask
let parentLayer = CALayer()
parentLayer.frame = view.bounds
parentLayer.backgroundColor = UIColor.white.cgColor // sets background of mask
view.layer.addSublayer(parentLayer)
let mask = CALayer()
mask.contents = UIImage(named: "twitter.png")?.cgImage
mask.contentsGravity = kCAGravityResizeAspect
mask.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
mask.anchorPoint = CGPoint(x: 0.5, y: 0.5)
mask.position = CGPoint(x: view.frame.size.width/2, y: view.frame.size.height/2)
parentLayer.mask = mask
This can be achieved by changing the background color of appDelegate's window...
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window!.backgroundColor = UIColor.blue