swift label only border left - swift

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)

Related

Add corner radius to view borders Swift

I have a UIView extension with a function that adds borders to specific edges (which I need). On that function, I am also trying to add cornerRadius if the borders are near each other.
Here is my function:
func addBorders(withEdges edges: [UIRectEdge],
withColor color: UIColor,
withThickness thickness: CGFloat) {
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight, .bottomRight, .bottomLeft[![\]][1]][1], cornerRadii: CGSize(width: 10, height: 10)).cgPath
layer.mask = maskLayer
let border = CAShapeLayer()
edges.forEach({ edge in
border.backgroundColor = color.cgColor
border.name = String(edge.rawValue)
switch edge {
case .left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.size.height)
break
case .right:
border.frame = CGRect(x: frame.size.width - thickness, y: 0, width: thickness, height: frame.size.height)
break
case .top:
border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: thickness)
break
case .bottom:
border.frame = CGRect(x: 0, y: frame.size.height - thickness, width: frame.size.width, height: thickness)
break
default:
break
}
layer.addSublayer(border)
})
border.path = maskLayer.path
}
This functions adds the borders well, as I need them, and let's say I need all the borders and corners (based on the hardcoded byRoundingCorners value), but the result is at if follows:
The cornerRadius is there, but the borders now are somehow the whole UIView not the thickness I've sent.
Can somebody help me?
Thank you for your time!
**
UPDATE
**
So I have updated my function to this:
func addBorders(withEdges edges: [UIRectEdge],
withColor color: UIColor,
withThickness thickness: CGFloat) {
edges.forEach({ edge in
let border = CALayer()
border.backgroundColor = color.cgColor
border.name = String(edge.rawValue)
switch edge {
case .left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.size.height)
border.cornerRadius = 10
border.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
border.masksToBounds = true
break
case .right:
border.frame = CGRect(x: frame.size.width - thickness, y: 0, width: thickness, height: frame.size.height)
border.cornerRadius = 10
border.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
border.masksToBounds = true
break
case .top:
border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: thickness)
border.cornerRadius = 10
border.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
border.masksToBounds = true
break
case .bottom:
border.frame = CGRect(x: 0, y: frame.size.height - thickness, width: frame.size.width, height: thickness)
border.cornerRadius = 10
border.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
border.masksToBounds = true
break
default:
break
}
layer.addSublayer(border)
})
and now the output is like that:
How can I achieve some normal corners?
if you look at the red lines that you’re drawing (closely, zoom on it with an app like “Zoom It”, you will see that the edges of that red border have been curved out, which i think that is not what you want).
here is what i came up with:
func addBorders(withEdges edges: [UIRectEdge],
withColor color: UIColor,
withThickness thickness: CGFloat,
cornerRadius: CGFloat) {
layer.borderColor = color.cgColor
layer.borderWidth = thickness
layer.cornerRadius = cornerRadius
edges.forEach({ edge in
switch edge {
case .left:
layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
case .right:
layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
case .top:
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
case .bottom:
layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
default:
break
}
})
}

How do add a bottom boarder to UIView Class?

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

UILabel - custom borders on rounded corners left, top and right

I am looking at adding borders top left and top right on UILabels.
What working solution is best ?
I have tried :
func draw(_ rect: CGRect) {
let cgContext = UIGraphicsGetCurrentContext()
cgContext?.move(to: CGPoint(x: rect.minX, y: rect.minY))
cgContext?.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
cgContext?.setStrokeColor(UIColor.red.cgColor)
cgContext?.setLineWidth(2.0)
cgContext?.strokePath()
}
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case .top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: thickness)
case .left:
border.frame = CGRect(x:0, y: self.frame.height - thickness, width: self.frame.width, height: thickness)
case .bottom:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: self.frame.height)
case .right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: self.frame.height)
default: break
}
border.backgroundColor = color.cgColor
self.addSublayer(border)
}
func addBorder2(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
border.backgroundColor = color.cgColor
//top
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 2)
//left
border.frame = CGRect(x: 0, y: 0, width: thickness, height: 2)
//right
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: 2)
self.addSublayer(border)
/* SWITCH CASE NOT WORKING !!!
switch edge {
case .top:
border.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 2)
case .bottom:
border.frame = CGRect(x: 0, y: self.frame.height - thickness, width: self.frame.width, height:2)
case .left:
border.frame = CGRect(x: 0, y: 0, width: thickness, height: 2)
case .right:
border.frame = CGRect(x: self.frame.width - thickness, y: 0, width: thickness, height: 2)
default: break
}
*/
}
Final result should be something like this below :
A border is needed around top left and top right. The bottom remains with no border.
You can set roundness to corners of any UIView using UIBezierPath.
setRoundness(ToCorners: [.topLeft, .bottomLeft], for: yourView, withRadius: customRadius)
Function:
func setRoundness(ToCorners corners: UIRectCorner, for view: UIView, withRadius radius: CGFloat) {
let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
view.layer.mask = mask
}

swift drawing borders around textview or labels

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

Position of UIView in ScrollView is wrong

I tried to add 4 views to scrollview to create horizontal paging view.
But I don't understand why between each view have white space which width equal width of each view. They should be close together.
walkthroughItems = {
let width = UIScreen.mainScreen().bounds.size.width
let height = UIScreen.mainScreen().bounds.size.height
return [
WalkthroughItem0(frame: CGRect(x: 0, y: 0, width: width,height: height)),
WalkthroughItem1(frame: CGRect(x: width, y: 0, width: width,height: height)),
WalkthroughItem2(frame: CGRect(x: width * 2, y: 0, width: width,height: height)),
WalkthroughItem3(frame: CGRect(x: width * 3, y: 0, width: width,height: height))
]
}()
for item in walkthroughItems {
scrollView.addSubview(item)
}
scrollView.contentSize = CGSize(width: UIScreen.mainScreen().bounds.size.width*4, height: UIScreen.mainScreen().bounds.size.height)