How do I customize a UITableview right, top and bottom border? - ios

How can I set right, left, top and bottom border with color on UITableview in swift?
Thanks,

Try this for full border:
yourtable.layer.masksToBounds = true
yourtable.layer.borderColor = UIColor( red: 153/255, green: 153/255, blue:0/255, alpha: 1.0 ).CGColor
yourtable.layer.borderWidth = 2.0
This is for bottom border:
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.darkGrayColor().CGColor
border.frame = CGRect(x: 0, y: yourtable.frame.size.height - width, width: yourtable.frame.size.width, height: yourtable.frame.size.height)
border.borderWidth = width
yourtable.layer.addSublayer(border)
yourtable.layer.masksToBounds = true

extension UIView {
func addBorderTop(size size: CGFloat, color: UIColor) {
addBorderUtility(x: 0, y: 0, width: frame.width, height: size, color: color)
}
func addBorderBottom(size size: CGFloat, color: UIColor) {
addBorderUtility(x: 0, y: frame.height - size, width: frame.width, height: size, color: color)
}
func addBorderLeft(size size: CGFloat, color: UIColor) {
addBorderUtility(x: 0, y: 0, width: size, height: frame.height, color: color)
}
func addBorderRight(size size: CGFloat, color: UIColor) {
addBorderUtility(x: frame.width - size, y: 0, width: size, height: frame.height, color: color)
}
private func addBorderUtility(x x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat, color: UIColor) {
let border = CALayer()
border.backgroundColor = color.CGColor
border.frame = CGRect(x: x, y: y, width: width, height: height)
layer.addSublayer(border)
}
}
I am going to open source my extension classes at some point.
Edit: Here you go, I update the functions in here https://github.com/goktugyil/EZSwiftExtensions

if you want to give the border to tableview with color use below code
for swift 3 :
yourTableView.layer.borderColor = UIColor.gray.cgColor
yourTableView.layer.borderWidth = 1.0

Related

How to add a bottom border in swift ios?

I am trying to add a bottom border to my tabs. Here is the code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .systemBackground
self.navigationItem.titleView = UIImageView(
image: UIImage(named: "Twitter")!.resize(25, 25)
)
let leftBarImage = UIImageView(
image: UIImage(named: "Profile")!.resize(35, 35)
)
leftBarImage.layer.borderWidth = 1
leftBarImage.layer.borderColor = UIColor.systemGray4.cgColor
leftBarImage.layer.cornerRadius = leftBarImage.frame.width / 2
leftBarImage.clipsToBounds = true
self.navigationItem.leftBarButtonItem = UIBarButtonItem(
customView: leftBarImage
)
let segmentedControl = UIStackView()
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
segmentedControl.axis = .horizontal
segmentedControl.alignment = .center
segmentedControl.distribution = .fillEqually
segmentedControl.backgroundColor = .green
segmentedControl.addBorder(
for: .Bottom,
color: UIColor.red.cgColor,
thickness: 20
)
for menuLabelText in ["For you", "Following"] {
let menuLabel = UILabel()
menuLabel.text = menuLabelText
menuLabel.textAlignment = .center
menuLabel.font = UIFont.boldSystemFont(ofSize: 16)
segmentedControl.addArrangedSubview(menuLabel)
}
self.view.addSubview(segmentedControl)
NSLayoutConstraint.activate([
segmentedControl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
segmentedControl.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor)
])
}
}
And the border logic,
import UIKit
extension UIView {
enum BorderSide {
case Left, Right, Top, Bottom
}
func addBorder(for side: BorderSide, color: CGColor, thickness: CGFloat) {
let border = CALayer()
border.backgroundColor = color
switch side {
case .Left:
border.frame = CGRect(
x: frame.minX,
y: frame.minY,
width: thickness,
height: frame.height
)
break
case .Right:
border.frame = CGRect(
x: frame.maxX,
y: frame.minY,
width: thickness,
height: frame.height
)
break
case .Top:
border.frame = CGRect(
x: frame.minX,
y: frame.minY,
width: frame.width,
height: thickness
)
break
case .Bottom:
border.frame = CGRect(
x: frame.minX,
y: frame.maxY,
width: frame.width,
height: thickness
)
break
}
layer.addSublayer(border)
}
}
The border is not showing at all. Here is a screenshot:
You can see the green background but not the red border beneath. Any help would be appreciated. Thanks!
First, let's look at what's wrong with the code...
The addBorder(...) func in your UIView extension uses the view's frame -- so, let's put a print() statement right before we try to add the border, to see the the view's frame:
segmentedControl.backgroundColor = .green
// print the frame of segmentedControl to debug console
print("segmentedControl Frame in viewDidLoad():", segmentedControl.frame)
segmentedControl.addBorder(
for: .Bottom,
color: UIColor.red.cgColor,
thickness: 20
)
You will see this in the debug console:
segmentedControl Frame in viewDidLoad(): (0.0, 0.0, 0.0, 0.0)
So your extension tries to set the frame of the layer:
border.frame = CGRect(
x: frame.minX, // minX == 0
y: frame.maxY, // maxY == 0
width: frame.width, // width == 0
height: thickness. // thickness == 20 (passed in call)
)
As we see, we end up with a layer frame of (x: 0.0, y: 0.0, width: 0.0, height: 20.0) ... we won't see anything, because it has no width.
So, let's try adding the border in viewDidLayoutSubviews().
Note that we'll move the segmentedControl creation outside of viewDidLoad() so we can reference it elsewhere. And, we'll leave the addBorder() with red where it was, then call it again with blue in viewDidLayoutSubviews():
class ViewController: UIViewController {
// create it here, so we can reference it outside of viewDidLoad()
let segmentedControl = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .systemBackground
self.title = "Bad Layout"
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
segmentedControl.axis = .horizontal
segmentedControl.alignment = .center
segmentedControl.distribution = .fillEqually
segmentedControl.backgroundColor = .green
// print the frame of segmentedControl to debug console
print("segmentedControl Frame in viewDidLoad():", segmentedControl.frame)
segmentedControl.addBorder(
for: .Bottom,
color: UIColor.red.cgColor,
thickness: 20
)
for menuLabelText in ["For you", "Following"] {
let menuLabel = UILabel()
menuLabel.text = menuLabelText
menuLabel.textAlignment = .center
menuLabel.font = UIFont.boldSystemFont(ofSize: 16)
segmentedControl.addArrangedSubview(menuLabel)
}
self.view.addSubview(segmentedControl)
NSLayoutConstraint.activate([
segmentedControl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
segmentedControl.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor)
])
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// print the frame of segmentedControl to debug console
print("segmentedControl Frame in viewDidLayoutSubviews():", segmentedControl.frame)
segmentedControl.addBorder(
for: .Bottom,
color: UIColor.blue.cgColor,
thickness: 20
)
}
}
extension UIView {
enum BorderSide {
case Left, Right, Top, Bottom
}
func addBorder(for side: BorderSide, color: CGColor, thickness: CGFloat) {
let border = CALayer()
border.backgroundColor = color
switch side {
case .Left:
border.frame = CGRect(
x: frame.minX,
y: frame.minY,
width: thickness,
height: frame.height
)
break
case .Right:
border.frame = CGRect(
x: frame.maxX,
y: frame.minY,
width: thickness,
height: frame.height
)
break
case .Top:
border.frame = CGRect(
x: frame.minX,
y: frame.minY,
width: frame.width,
height: thickness
)
break
case .Bottom:
border.frame = CGRect(
x: frame.minX,
y: frame.maxY,
width: frame.width,
height: thickness
)
break
}
layer.addSublayer(border)
}
}
Now, we see two "print frame" outputs:
segmentedControl Frame in viewDidLoad(): (0.0, 0.0, 0.0, 0.0)
segmentedControl Frame in viewDidLayoutSubviews(): (0.0, 97.66666666666667, 393.0, 19.333333333333332)
Unfortunately, this is the result:
The blue layer is positioned way below the bottom of the stack view / labels.
That's happening because the extension is using frame.maxY -- which is frame.origin.y + frame.size.height -- and the frame's origin.y is 97.66666666666667 (it's top is below the navigation bar).
You could use the same addBorder() approach, by calling it after the views have been laid out (that is, after the frames have been set), and modifying the extension to use the view's bounds instead of frame:
case .Bottom:
border.frame = CGRect(
x: bounds.minX,
y: bounds.maxY,
width: bounds.width,
height: thickness
)
break
and we get this:
However... as should be obvious, this is really not a good approach. Worth noting also is that the layer appears outside the bounds of the view. So, if you were to add a subview constrained to the bottom of segmentedControl, the top of that view would be covered by the 20-point tall "border" layer.
My guess is that you are also going to want to be able to tap the labels... possible you also want to move that "border" to show only under the selected label... etc.
Do some searching / exploring how to subclass UIView so it handles all of that by itself.

I want to use only top border line and right and left top corners . I did it but corners colors does not appear. Can anybody help me?

extension UIView {
func roundCorners(view :UIView, corners: UIRectCorner, 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
path.close()
let color = UIColor.white
color.setStroke()
path.stroke()
}
enum ViewSide: String {
case Left = "Left", Right = "Right", Top = "Top", Bottom = "Bottom"
}
func addBorder(toSide side: ViewSide, withColor color: CGColor, andThickness thickness: CGFloat) {
let border = CALayer()
border.borderColor = color
border.name = side.rawValue
switch side {
case .Left: border.frame = CGRect(x: 0, y: 0, width: thickness, height: frame.height)
case .Right: border.frame = CGRect(x: frame.width - thickness, y: 0, width: thickness, height: frame.height)
case .Top: border.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness)
case .Bottom: border.frame = CGRect(x: 0, y: frame.height - thickness, width: frame.width, height: thickness)
}
border.borderWidth = thickness
layer.addSublayer(border)
}
func removeBorder(toSide side: ViewSide) {
guard let sublayers = self.layer.sublayers else { return }
var layerForRemove: CALayer?
for layer in sublayers {
if layer.name == side.rawValue {
layerForRemove = layer
}
}
if let layer = layerForRemove {
layer.removeFromSuperlayer()
}
}
}
class TabbarView: UIView {
var viewColor = UIView()
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
addBorder(toSide: .Bottom, withColor:CGColor.init(gray: 100/255, alpha: 100/255), andThickness: 1)
addBorder(toSide: .Top, withColor: CGColor.init(gray: 100/255, alpha: 100/255), andThickness: 1)
addBorder(toSide: .Left, withColor: CGColor.init(gray: 100/255, alpha: 100/255), andThickness: 1)
addBorder(toSide: .Right, withColor: CGColor.init(gray: 100/255, alpha: 100/255), andThickness: 1)
self.roundCorners(view: self, corners: [.topLeft, .topRight], radius: 20)
removeBorder(toSide: .Bottom)
removeBorder(toSide: .Left)
removeBorder(toSide: .Right)
}

UIBezierPath does not change color and is not added to the view

There is an implementation like this:
class EllupseTest: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let views = MyView(frame: CGRect(x: 36.62, y: 77.54, width: 303.19, height: 495.93))
views.backgroundColor = .clear
views.rotate(degrees: 45.84)
view.addSubview(views)
}
}
class Ellipse: UIView {
override func draw(_ rect: CGRect) {
let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 303.19, height: 495.93))
UIColor.cyan.setFill()
path.fill()
}
}
But if I want to change the color, the rectangle will be colored, not the ellipse itself (that's why I put views.backgroundColor = .clear for a while) How can I change the color of the ellipse itself, and not its rectangle?
I read that you can use PaintCode, but I don't understand how to add it to the view via view.addSubview (). How to add to a view
Here is the code from PaintCode
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
//// Color Declarations
let color = UIColor(red: 1.000, green: 0.773, blue: 0.620, alpha: 1.000)
//// Oval Drawing
context.saveGState()
context.translateBy(x: 610, y: -139)
context.rotate(by: 90 * CGFloat.pi/180)
let ovalPath = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 314.87, height: 515.04))
color.setFill()
ovalPath.fill()
context.restoreGState()
I work with UIBezierPath for the first time)

Indent UITextField text and placeholder

I have programmatically created a UITextField in my UITableView footer like so:
let customView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 50))
self.textArea = UITextField(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width - 50, height: 50))
self.textArea.placeholder = "Add Item"
self.textArea.layer.borderColor = UIColor.gray.cgColor
self.textArea.layer.borderWidth = 1
customView.addSubview(self.textArea)
let button = UIButton(frame: CGRect(x: self.view.bounds.width - 50, y: 0, width: 50, height: 50))
button.setTitle("Add", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = UIColor(displayP3Red: 3.0 / 255.0, green: 0.0 / 255.0, blue: 113.0 / 255.0, alpha: 1.0)
button.addTarget(self, action: #selector(self.addWishListItem), for: .touchUpInside)
customView.addSubview(button)
self.tableView.tableFooterView = customView
My question is, how do I indent the UITextField text and placeholder text so it's not right at the edge of the left side?
I found this:
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: self.textArea.frame.height))
self.textArea.leftView = paddingView
self.textArea.leftViewMode = .always
Is there a better way on doing this?
You can create a customTextField and override the rects for text, placeholder, border.. basically everything.
import UIKit
class CustomTextField: UITextField {
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return CGRect.init(x: 10, y: -10, width: bounds.width, height: bounds.height)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect.init(x: 10, y: -10, width: bounds.width, height: bounds.height)
}
}
You control the position of it by changing x and y values

single border for UITextField within a UIcollectionViewCell (swift 3 xcode)

I'm trying to apply a single bottom border to a textField that sits within one of my collectionViewCells. Here is the code:
class AddressCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func basicTextField(placeHolderString: String) -> UITextField {
let textField = UITextField()
textField.font = UIFont.boldSystemFont(ofSize: 12)
textField.attributedPlaceholder = NSAttributedString(string: placeHolderString, attributes:[NSForegroundColorAttributeName: UIColor.lightGray, NSFontAttributeName: UIFont.boldSystemFont(ofSize: 12)])
textField.backgroundColor = UIColor.white
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
backgroundColor = UIColor.white
layer.addBorder(edge: UIRectEdge.bottom, color: .black, thickness: 0.5)
let streetTextfield = basicTextField(placeHolderString: "street")
addSubview(streetTextfield)
}
}
I am using an extension that enables me to apply a single border, which has worked great so far:
extension CALayer {
func addBorder(edge: UIRectEdge, color: UIColor, thickness: CGFloat) {
let border = CALayer()
switch edge {
case UIRectEdge.top:
border.frame = CGRect.init(x: 0, y: 0, width: frame.width, height: thickness)
break
case UIRectEdge.bottom:
border.frame = CGRect.init(x: 0, y: frame.height - thickness, width: frame.width, height: thickness)
break
case UIRectEdge.left:
border.frame = CGRect.init(x: 0, y: 0, width: thickness, height: frame.height)
break
case UIRectEdge.right:
border.frame = CGRect.init(x: frame.width - thickness, y: 0, width: thickness, height: frame.height)
break
default:
break
}
border.backgroundColor = color.cgColor;
self.addSublayer(border)
}
}
When i simply add a borderWidth to the textfield like this:
textField.layer.borderWidth = 0.5
I get a border and it renders fine. However, when i apply the extension to add a bottom border, like this:
textField.layer.addBorder(edge: UIRectEdge.bottom, color: .black, thickness: 0.5)
the border doesn't apply for some reason.
I'm not sure about the extension, but I have a working solution for a TextField with a bottom border.
basically create a class BottomBorderedTextField subclassing UITextField and insert the following code:
class BottomBorderedTextField: UITextField {
override func draw(_ rect: CGRect) {
super.draw(rect)
//this is the color of the bottom border. Change to whatever you what
let color: UIColor = UIColor.rgb(red: 230, green: 230, blue: 230)
let bottomBorder = CALayer()
bottomBorder.frame = CGRect(x: 0, y: bounds.size.height - 1, width: bounds.size.width, height: 2)
bottomBorder.backgroundColor = color.cgColor
self.layer.addSublayer(bottomBorder)
}
}
Then on your basicTextField function set the return type to the BottomBorderedTextField class you just made. so your new code will look like:
func basicTextField(placeHolderString: String) -> BottomBorderedTextField {
let textField = BottomBorderedTextField()
textField.font = UIFont.boldSystemFont(ofSize: 12)
textField.attributedPlaceholder = NSAttributedString(string: placeHolderString, attributes:[NSForegroundColorAttributeName: UIColor.lightGray, NSFontAttributeName: UIFont.boldSystemFont(ofSize: 12)])
textField.backgroundColor = UIColor.white
textField.translatesAutoresizingMaskIntoConstraints = false
return textField
}
I wrote a UIView extension that accomplishes the same thing:
extension UIView {
func addBottomBorder(width: CGFloat, color: UIColor, alpha: CGFloat) {
let border = CALayer()
let width = width
border.borderColor = color.withAlphaComponent(alpha).cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
}
}
Usage:
usernameField.addBottomBorder(width: 2.0, color: UIColor.white, alpha: 0.5)
Also, seems within setupViews() you're calling:
layer.addBorder... //this is being called on the collection view cells layer
As opposed to:
streetTextfield.layer.addBorder...

Resources