I have a view class that I apply to various UIViews in UITableViews throughout my Storyboard.
class ListContainerView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
// Set the thickness of the border
let thickness: CGFloat = 0.5
// Set the color of the border
let color: CGColor = UIColor.lightGray.cgColor
// Add top border to the ListContainerView
let topBorder = CALayer()
topBorder.frame = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: thickness)
topBorder.backgroundColor = color
self.layer.addSublayer(topBorder)
// Add bottom border to ListContainerView
let bottomBorder = CALayer()
bottomBorder.frame = CGRect(x: 0.0, y: self.frame.size.height - thickness, width: self.frame.size.width, height: thickness)
bottomBorder.backgroundColor = color
self.layer.addSublayer(bottomBorder)
}
}
As of now, the code properly adds a top border to the UIView. However, it is not adding the bottom border. I have tried numerous tactics from various StackOverflow answers but none have been successful. What am I doing incorrectly? How can I go about resolving this issue?
UPDATE: I have corrected the initial typo in my code. Additionally, here is a screenshot of an example of where this view is being accessed. In the screenshot, both of the Post View(s) are assigned the class ListContainerView.
Thank you in advance for any help you are able to provide!
Actually, I have the following UIView subclass similar to yours. Try it. It always works.
import UIKit
class TopBotView: UIView {
override func draw(_ rect: CGRect) {
let tHeight = self.frame.size.height
let topLayer = CALayer()
topLayer.frame = CGRect(x: 0.0, y: tHeight - 0.5, width: self.frame.size.width, height: 0.5)
topLayer.backgroundColor = UIColor.white.withAlphaComponent(0.3).cgColor
topLayer.masksToBounds = true
self.layer.addSublayer(topLayer)
let bottomLayer = CALayer()
bottomLayer.frame = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: 0.5)
bottomLayer.backgroundColor = UIColor.white.withAlphaComponent(0.3).cgColor
bottomLayer.masksToBounds = true
self.layer.addSublayer(bottomLayer)
}
}
UPDATE
If you want to surround all sides with a thin line, I have the following subclass.
import UIKit
class AllAroundView: UIView {
override func draw(_ rect: CGRect) {
let tHeight = self.frame.size.height
let topLayer = CALayer()
topLayer.frame = CGRect(x: 0.0, y: tHeight - 0.5, width: self.frame.size.width, height: 0.5)
topLayer.backgroundColor = UIColor.black.cgColor
topLayer.masksToBounds = true
self.layer.addSublayer(topLayer)
let bottomLayer = CALayer()
bottomLayer.frame = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: 0.5)
bottomLayer.backgroundColor = UIColor.black.cgColor
bottomLayer.masksToBounds = true
self.layer.addSublayer(bottomLayer)
let leftLayer = CALayer()
leftLayer.frame = CGRect(x: 0.0, y: 0.0, width: 0.5, height: self.frame.size.height)
leftLayer.backgroundColor = UIColor.black.cgColor
leftLayer.masksToBounds = true
self.layer.addSublayer(leftLayer)
let rightLayer = CALayer()
rightLayer.frame = CGRect(x: self.frame.size.width - 0.5, y: 0.0, width: 0.5, height: self.frame.size.height)
rightLayer.backgroundColor = UIColor.black.cgColor
rightLayer.masksToBounds = true
self.layer.addSublayer(rightLayer)
}
}
bottomBorder.frame = CGRect(x: 0.0, y: self.frame.size.width - thickness, width: self.frame.size.width, height: thickness)
shouldn't it be
bottomBorder.frame = CGRect(x: 0.0, y: self.frame.size.height - thickness, width: self.frame.size.width, height: thickness)
unless your UIView is a square
Related
How can i draw custom oval shape like in below image in swift (not swiftUI).
Thank you in advance
I have tried to clone similar view using UIView and was able to create similar UI as you have stated on screenshot
And here is my code snippet
let width: CGFloat = view.frame.size.width
let height: CGFloat = view.frame.size.height
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height), cornerRadius: 0)
let rect = CGRect(x: width / 2 - 150, y: height / 2.5 - 100, width: 300, height: 400)
let circlePath = UIBezierPath(ovalIn: rect)
path.append(circlePath)
path.usesEvenOddFillRule = true
let fillLayer = CAShapeLayer()
fillLayer.path = path.cgPath
fillLayer.fillRule = .evenOdd
fillLayer.fillColor = UIColor.red.cgColor
fillLayer.opacity = 0.5
view.layer.addSublayer(fillLayer)
Hope this helps you. Good day.
You can draw oval path like this
class CustomOval: UView {
override func drawRect(rect: CGRect) {
var ovalPath = UIBezierPath(ovalIn: rect)
UIColor.gray.setFill()
ovalPath.fill()
}
}
I Have tried to Center CAShapeLayer in UIImageView but didn't succeed and I have find solution that I have added it(CAShapeLayer) to UIVIew and then Add UIVIew to ImageView and its worked !!!
Is this good practices ? any suggestion
func Draw(Fn:Bool) {
let theCenter = CGPoint(x: img.bounds.midX, y: img.bounds.midY)
// Create Layer To Add The CAShapeLayer On it So I can Center it
let DrawingLayer = UIView(frame: CGRect(x: img.bounds.midX, y: img.bounds.midY, width: 140, height: 80))
DrawingLayer.center = theCenter
img.addSubview(DrawingLayer)
// Create The Drawing
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 140, height: 80)).cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 3.0
// Add Drawing to UIView
DrawingLayer.layer.addSublayer(shapeLayer)
// Add UIView To ImageView
img.addSubview(DrawingLayer)
}
I was using this code :
func DrawArmor(Fn:Bool) {
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 140, height: 80)).cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 3.0
if (Fn) {
shapeLayer.name = "Armor Friendly"
shapeLayer.strokeColor = UIColor.blue.cgColor
}else{
shapeLayer.name = "ArmorEnemy"
shapeLayer.strokeColor = UIColor.red.cgColor
}
img.layer.addSublayer(shapeLayer)
}
but the Drawing showing on right bottom corner which mean the ancherpoint is not the center
Find a solution
shapeLayer.frame = CGRect(x: img.bounds.midX, y: img.bounds.midY, width: 140, height: 80)
shapeLayer.position = CGPoint(x: img.bounds.midX, y: img.bounds.midY)
I have set a size of the CAShapeLayer and then set the position for it and its work
myLabel.layer.borderWidth = 0.5
myLabel.layer.borderColor = UIColor.green.cgColor
Thats a simple answer of drawing a border. However the issue is I need to draw the border only to the right and bottom. I also need another label to draw border on top and right. How can I draw border to specific sides, not all 4 sides?
Add a shape layer and draw the lines in that layer. Note that layers do not participate in auto layout so you need to put your code in viewDidLayoutSubviews or subclass uilabel and do this in layout subviews. Here is a playground example using the UILabel subclass:
import PlaygroundSupport
import UIKit
class L: UILabel {
var strokeColor = UIColor.blue
var strokeWidth = CGFloat(0.5)
private lazy var labelBorderLayer:CAShapeLayer = {
let shapeLayer = CAShapeLayer()
self.layer.addSublayer(shapeLayer)
return shapeLayer
}()
override func layoutSubviews() {
super.layoutSubviews()
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: bounds.size.height))
path.addLine(to: CGPoint(x: bounds.size.width, y: bounds.size.height))
path.addLine(to: CGPoint(x: bounds.size.width, y: 0))
labelBorderLayer.path = path
labelBorderLayer.strokeColor = strokeColor.cgColor
labelBorderLayer.lineWidth = strokeWidth
labelBorderLayer.fillColor = UIColor.clear.cgColor
}
}
let v = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
let l = L(frame: v.frame.insetBy(dx: 50, dy: 80))
v.addSubview(l)
l.textColor = .white
l.textAlignment = .center
l.text = "gjgkjgjgjgj"
v.backgroundColor = .red
PlaygroundPage.current.liveView = v
Im not sure if you can do that with the actual label border. But but you can do something similar to this
public enum UIButtonBorderSide {
case Top, Bottom, Left, Right
}
extension UIButton {
public func addBorder(side: UIButtonBorderSide, color: UIColor, width: CGFloat) {
let border = CALayer()
border.backgroundColor = color.CGColor
switch side {
case .Top:
border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: width)
case .Bottom:
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width)
case .Left:
border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height)
case .Right:
border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height)
}
self.layer.addSublayer(border)
}
}
Thats for UIButton, But you can probably adapt it easily.
Source: https://gist.github.com/Isuru-Nanayakkara/496d5713e61125bddcf5
Hope this helps
good morning together,
i have a tableview like this:
Example:
in cell one i have got an red text label on the right side.
left from it i include an image like a grey line.
with this code i can set a complete green border:
cell.Label.layer.borderWidth = 0.5
cell.Label.layer.borderColor = UIColor.greenColor().CGColor
can i set only a border on the left side from this text label?
i use swift ios8 - so, i need a swift solution
Here is an extension you can add to your project:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
var border = CALayer()
switch edge {
case UIRectEdge.Top:
border.frame = CGRectMake(0, 0, CGRectGetHeight(self.frame), thickness)
break
case UIRectEdge.Bottom:
border.frame = CGRectMake(0, CGRectGetHeight(self.frame) - thickness, UIScreen.mainScreen().bounds.width, thickness)
break
case UIRectEdge.Left:
border.frame = CGRectMake(0, 0, thickness, CGRectGetHeight(self.frame))
break
case UIRectEdge.Right:
border.frame = CGRectMake(CGRectGetWidth(self.frame) - thickness, 0, thickness, CGRectGetHeight(self.frame))
break
default:
break
}
border.backgroundColor = color.CGColor;
self.addSublayer(border)
}
}
And the use it like this:
cell.Label.layer.addBorder(UIRectEdge.Top, color: UIColor.greenColor(), thickness: 0.5)
FOR SWIFT 3, 4 & 5:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.height, height: thickness)
break
case UIRectEdge.bottom:
border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: UIScreen.main.bounds.width, height: thickness)
break
case UIRectEdge.left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
break
case UIRectEdge.right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
break
default:
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
Swift 3 version:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: thickness)
break
case UIRectEdge.bottom:
border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: self.frame.width, height: thickness)
break
case UIRectEdge.left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
break
case UIRectEdge.right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
break
default:
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
Or use IBDesignable (see answer below)
Swift 4 Version:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: thickness)
case UIRectEdge.bottom:
border.frame = CGRect(x: 0, y: self.bounds.height - thickness, width: self.bounds.width, height: thickness)
case UIRectEdge.left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.bounds.height)
case UIRectEdge.right:
border.frame = CGRect(x: self.bounds.width - thickness, y: 0, width: thickness, height: self.bounds.height)
default:
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
These solutions worked for me, however I was using tableview rows and the height of the row was dynamically set based on how much content was in the rows.
For the above solutions I found that if the size of the row was dynamically increased because of the content within the row, the border's size didn't increase. So I had an incomplete border on rows that were larger than normal.
I came across this solution from #Debaprio B at this link (Oct 11, 2017 post):
UIView set only side borders
His solution worked for me, and the border's sized changed appropriately when my row's size increased dynamically. I'm sharing #Debaprio's answer below for brevity:
public extension UIView {
// Border type and arbitrary tag values to identify UIView borders as subviews
public enum BorderType: Int {
case left = 20000
case right = 20001
case top = 20002
case bottom = 20003
}
public func addBorder(borderType: BorderType, width: CGFloat, color: UIColor {
// figure out frame and resizing based on border type
var autoresizingMask: UIViewAutoresizing
var layerFrame: CGRect
switch borderType {
case .left:
layerFrame = CGRect(x: 0, y: 0, width: width, height: self.bounds.height)
autoresizingMask = [ .flexibleHeight, .flexibleRightMargin ]
case .right:
layerFrame = CGRect(x: self.bounds.width - width, y: 0, width: width, height: self.bounds.height)
autoresizingMask = [ .flexibleHeight, .flexibleLeftMargin ]
case .top:
layerFrame = CGRect(x: 0, y: 0, width: self.bounds.width, height: width)
autoresizingMask = [ .flexibleWidth, .flexibleBottomMargin ]
case .bottom:
layerFrame = CGRect(x: 0, y: self.bounds.height - width, width: self.bounds.width, height: width)
autoresizingMask = [ .flexibleWidth, .flexibleTopMargin ]
}
// look for the existing border in subviews
var newView: UIView?
for eachSubview in self.subviews {
if eachSubview.tag == borderType.rawValue {
newView = eachSubview
break
}
}
Apart from using an extension, you can also use #IBDesignable / #IBInspectable to create a subclass from UILabel and draw the border there. This has some advantages, because you can then use the Interface Builder to style your label and it is shown directly in the Storyboard.
#IBDesignable
class LeftBorderedLabel: UILabel {
#IBInspectable var blockColor: UIColor = UIColor.black {
didSet{
let border = CALayer()
border.frame = CGRect(x: 0, y: 0, width: 15, height: self.frame.height)
border.backgroundColor = blockColor.cgColor;
self.layer.addSublayer(border)
}
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
}
}
Example in interface builder (example is for a tableViewCell):
Here is an extension you can add to your project.
SWIFT 3 :-
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: thickness)
break
case UIRectEdge.bottom:
border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: self.frame.width, height: thickness)
break
case UIRectEdge.left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
break
case UIRectEdge.right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
break
default:
//For Center Line
border.frame = CGRect(x: self.frame.width/2 - thickness, y: 0, width: thickness, height: self.frame.height)
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
And the use it like this:
labelName.layer.addBorder(edge: UIRectEdge.right, color: UIColor.black, thickness: 1.5)
If you want to draw a line on Center, then you can set following frame in any of the scenario :-
border.frame = CGRect(x: self.frame.width/2 - thickness, y: 0, width: thickness, height: self.frame.height)
Hi i like to add an CALayer Shadow to an View where the shadow was left and right of the view the simplest way was:
someView.layer.shadowColor = [[UIColor blackColor] CGColor];
someView.layer.shadowOffset = CGSizeMake(0.0f,0.0f);
someView.layer.shadowOpacity = 1.0f;
someView.layer.shadowRadius = 10.0f;
someView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:someView.bounds] CGPath];
but i will add a bigger shadow as like a shadow when i increase the shadowRadius it do not look good. How i can make a shadow that looks good at left and right.
I think 10 is a pretty big shadow radius, try 3 or 4 instead, also opacity I usually use 0.7:
someView.layer.shadowColor = [[UIColor blackColor] CGColor];
someView.layer.shadowOffset = CGSizeMake(0.0f,0.0f);
someView.layer.shadowOpacity = 0.7f;
someView.layer.shadowRadius = 4.0f;
If you want the shadow only on left and right, then inset the rectangle on the top and bottom so the top and bottom shadow are hidden behind your view:
CGRect shadowRect = CGRectInset(someView.bounds, 0, 4); // inset top/bottom
someView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:shadowRect] CGPath];
I'm not really sure if that's what you wanted.
swift 3.0 version
imageView.layer.shadowOpacity = 0.8
imageView.layer.shadowOffset = CGSize(width: 0, height: 3)
imageView.layer.shadowRadius = 4.0
let shadowRect: CGRect = imageView.bounds.insetBy(dx: 0, dy: 4)
imageView.layer.shadowPath = UIBezierPath(rect: shadowRect).cgPath
progrmr's answer was very helpful, cheers!
I made a slide-out menu and I had a problem with the shadow surrounding my VC and disrupting the navigation bar. Turned out I had to inset the shadow layer.
Here's my solution using swift:
rightViewController!.view.layer.shadowOpacity = 0.8
rightViewController!.view.layer.shadowOffset = CGSizeMake(0, 3)
rightViewController!.view.layer.shadowRadius = 4.0
let shadowRect: CGRect = CGRectInset(rightViewController!.view.bounds, 0, 4); // inset top/bottom
rightViewController!.view.layer.shadowPath = UIBezierPath(rect: shadowRect).CGPath
I created a shadow that can be used at the top/bottom or left/right, without prejudice at all. No shadow will appear in the left/right if you choose top/bottom, and so on. I hope I helped. Here's the code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let imgView = UIImageView(frame: CGRect(x: self.view.bounds.midX - 150, y: self.view.bounds.midY - 150, width: 300, height: 300))
imgView.contentMode = .scaleToFill
imgView.image = UIImage(named: "name image")
self.view.addSubview(imgView)
//You can put left/right or top/bottom
imgView.addShadow(sides: .leftRight, constant: 10, alpha: 5)
}
}
extension UIView{
func addShadow(sides: Sides, constant: Int, alpha: Int){
if(constant > 0 && alpha > 0){
switch sides {
case .leftRight:
let view = UIView(frame: CGRect(x: -CGFloat(constant), y: 0, width: self.bounds.width + CGFloat((constant * 2)), height: self.bounds.height))
self.addSubview(view)
addMask(sides: .leftRight, view: view, constant: constant, shadowRadius: alpha)
case .topBottom:
let view = UIView(frame: CGRect(x: 0, y: -CGFloat(constant), width: self.bounds.width , height: self.bounds.height + CGFloat(constant * 2)))
self.addSubview(view)
addMask(sides: .topBottom, view: view, constant: constant, shadowRadius: alpha)
}
}
}
private func addMask(sides:Sides, view: UIView, constant: Int, shadowRadius:Int){
let mutablePath = CGMutablePath()
let mask = CAShapeLayer()
switch sides {
case .leftRight:
mutablePath.addRect(CGRect(x: -CGFloat(constant), y: 0, width: view.bounds.width, height: view.bounds.height))
mutablePath.addRect(CGRect(x: CGFloat(constant) , y: 0, width: view.bounds.width , height: view.bounds.height))
case .topBottom:
mutablePath.addRect(CGRect(x: 0, y: -CGFloat(constant), width: view.bounds.width, height: view.bounds.height))
mutablePath.addRect(CGRect(x: 0, y: CGFloat(constant) , width: view.bounds.width , height: view.bounds.height))
}
mask.path = mutablePath
mask.fillRule = CAShapeLayerFillRule.evenOdd
mask.fillColor = UIColor(white:1.0, alpha: 0.2).cgColor
view.layer.mask = mask
view.layer.addSublayer(mask)
mask.shadowColor = UIColor.black.cgColor
mask.shadowRadius = CGFloat(shadowRadius)
mask.shadowOpacity = 1.0
mask.shadowOffset = CGSize(width: 0, height: 0)
}
}
enum Sides{
case leftRight
case topBottom
}
You just need to call the extension UIView, which has the addShadow() function and pass the parameters you want.