Swift
Create UIView extension
private var bottomLineColorAssociatedKey : UIColor = .black
private var topLineColorAssociatedKey : UIColor = .black
private var rightLineColorAssociatedKey : UIColor = .black
private var leftLineColorAssociatedKey : UIColor = .black
extension UIView {
@IBInspectable var bottomLineColor: UIColor {
get {
if let color = objc_getAssociatedObject(self, &bottomLineColorAssociatedKey) as? UIColor {
return color
} else {
return .black
}
} set {
objc_setAssociatedObject(self, &bottomLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
@IBInspectable var bottomLineWidth: CGFloat {
get {
return self.bottomLineWidth
}
set {
DispatchQueue.main.async {
self.addBottomBorderWithColor(color: self.bottomLineColor, width: newValue)
}
}
}
@IBInspectable var topLineColor: UIColor {
get {
if let color = objc_getAssociatedObject(self, &topLineColorAssociatedKey) as? UIColor {
return color
} else {
return .black
}
} set {
objc_setAssociatedObject(self, &topLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
@IBInspectable var topLineWidth: CGFloat {
get {
return self.topLineWidth
}
set {
DispatchQueue.main.async {
self.addTopBorderWithColor(color: self.topLineColor, width: newValue)
}
}
}
@IBInspectable var rightLineColor: UIColor {
get {
if let color = objc_getAssociatedObject(self, &rightLineColorAssociatedKey) as? UIColor {
return color
} else {
return .black
}
} set {
objc_setAssociatedObject(self, &rightLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
@IBInspectable var rightLineWidth: CGFloat {
get {
return self.rightLineWidth
}
set {
DispatchQueue.main.async {
self.addRightBorderWithColor(color: self.rightLineColor, width: newValue)
}
}
}
@IBInspectable var leftLineColor: UIColor {
get {
if let color = objc_getAssociatedObject(self, &leftLineColorAssociatedKey) as? UIColor {
return color
} else {
return .black
}
} set {
objc_setAssociatedObject(self, &leftLineColorAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
@IBInspectable var leftLineWidth: CGFloat {
get {
return self.leftLineWidth
}
set {
DispatchQueue.main.async {
self.addLeftBorderWithColor(color: self.leftLineColor, width: newValue)
}
}
}
func addTopBorderWithColor(color: UIColor, width: CGFloat) {
let border = CALayer()
border.name = "topBorderLayer"
removePreviouslyAddedLayer(name: border.name ?? "")
border.backgroundColor = color.cgColor
border.frame = CGRect(x: 0, y : 0,width: self.frame.size.width, height: width)
self.layer.addSublayer(border)
self.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: UnsafeMutableRawPointer(bitPattern: 1111) )
}
func addRightBorderWithColor(color: UIColor, width: CGFloat) {
let border = CALayer()
border.name = "rightBorderLayer"
removePreviouslyAddedLayer(name: border.name ?? "")
border.backgroundColor = color.cgColor
border.frame = CGRect(x: self.frame.size.width - width, y: 0, width : width, height :self.frame.size.height)
self.layer.addSublayer(border)
self.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: UnsafeMutableRawPointer(bitPattern: 2222) )
}
func addBottomBorderWithColor(color: UIColor, width: CGFloat) {
let border = CALayer()
border.name = "bottomBorderLayer"
removePreviouslyAddedLayer(name: border.name ?? "")
border.backgroundColor = color.cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height - width,width : self.frame.size.width,height: width)
self.layer.addSublayer(border)
self.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: UnsafeMutableRawPointer(bitPattern: 3333) )
}
func addLeftBorderWithColor(color: UIColor, width: CGFloat) {
let border = CALayer()
border.name = "leftBorderLayer"
removePreviouslyAddedLayer(name: border.name ?? "")
border.backgroundColor = color.cgColor
border.frame = CGRect(x:0, y:0,width : width, height : self.frame.size.height)
self.layer.addSublayer(border)
self.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: .new, context: UnsafeMutableRawPointer(bitPattern: 4444) )
}
override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let objectView = object as? UIView,
objectView === self,
keyPath == #keyPath(UIView.bounds) {
switch context {
case UnsafeMutableRawPointer(bitPattern: 1111):
for border in self.layer.sublayers ?? [] {
if border.name == "topBorderLayer" {
border.frame = CGRect(x: 0, y : 0,width: self.frame.size.width, height: border.frame.height)
}
}
case UnsafeMutableRawPointer(bitPattern: 2222):
for border in self.layer.sublayers ?? [] {
if border.name == "rightBorderLayer" {
border.frame = CGRect(x: self.frame.size.width - border.frame.width, y: 0, width : border.frame.width, height :self.frame.size.height)
}
}
case UnsafeMutableRawPointer(bitPattern: 3333):
for border in self.layer.sublayers ?? [] {
if border.name == "bottomBorderLayer" {
border.frame = CGRect(x: 0, y: self.frame.size.height - border.frame.height,width : self.frame.size.width,height: border.frame.height)
}
}
case UnsafeMutableRawPointer(bitPattern: 4444):
for border in self.layer.sublayers ?? [] {
if border.name == "leftBorderLayer" {
border.frame = CGRect(x:0, y:0,width : border.frame.width, height : self.frame.size.height)
}
}
default:
break
}
}
}
func removePreviouslyAddedLayer(name : String) {
if self.layer.sublayers?.count ?? 0 > 0 {
self.layer.sublayers?.forEach {
if $0.name == name {
$0.removeFromSuperlayer()
}
}
}
}
}
Objective C
Create category class of UIView
UIView+Border.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
@interface UIView (Border)
@property (nonatomic) IBInspectable UIColor *topLineColor;
@property (nonatomic) IBInspectable CGFloat topLineWidth;
@property (nonatomic) IBInspectable UIColor *bottomLineColor;
@property (nonatomic) IBInspectable CGFloat bottomLineWidth;
@property (nonatomic) IBInspectable UIColor *rightLineColor;
@property (nonatomic) IBInspectable CGFloat rightLineWidth;
@property (nonatomic) IBInspectable UIColor *leftLineColor;
@property (nonatomic) IBInspectable CGFloat leftLineWidth;
- (void)addBottomBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;
- (void)addLeftBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;
- (void)addRightBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;
- (void)addTopBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth;
@end
UIView+Border.m
static void *topBorderContext = &topBorderContext;
static void *bottomBorderContext = &bottomBorderContext;
static void *leftBorderContext = &leftBorderContext;
static void *rightBorderContext = &rightBorderContext;
static char bottomLineColorKey,topLineColorKey,rightLineColorKey,leftLineColorKey;
@implementation UIView(Utility)
@dynamic borderColor,borderWidth,cornerRadius,bottomLineWidth,topLineWidth,rightLineWidth,leftLineWidth;
-(void)setBorderColor:(UIColor *)borderColor{
[self.layer setBorderColor:borderColor.CGColor];
}
-(void)setBorderWidth:(CGFloat)borderWidth{
[self.layer setBorderWidth:borderWidth];
}
-(void)setCornerRadius:(CGFloat)cornerRadius{
[self.layer setCornerRadius:cornerRadius];
}
// for Bottom Line
- (UIColor *)bottomLineColor {
return objc_getAssociatedObject(self, &bottomLineColorKey);
}
- (void)setBottomLineColor:(UIColor *)bottomLineColor {
objc_setAssociatedObject(self, &bottomLineColorKey,
bottomLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(void)setBottomLineWidth:(CGFloat)bottomLineWidth {
[self addBottomBorderWithColor:[self bottomLineColor] andWidth:bottomLineWidth];
}
// for top Line
- (UIColor *)topLineColor {
return objc_getAssociatedObject(self, &topLineColorKey);
}
- (void)setTopLineColor:(UIColor *)topLineColor {
objc_setAssociatedObject(self, &topLineColorKey,
topLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setTopLineWidth:(CGFloat)topLineWidth{
[self addTopBorderWithColor:[self topLineColor] andWidth:topLineWidth];
}
// for right Line
- (UIColor *)rightLineColor {
return objc_getAssociatedObject(self, &rightLineColorKey);
}
-(void)setRightLineColor:(UIColor *)rightLineColor {
objc_setAssociatedObject(self, &rightLineColorKey,
rightLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(void)setRightLineWidth:(CGFloat)rightLineWidth{
[self addRightBorderWithColor:[self rightLineColor] andWidth:rightLineWidth];
}
// for left Line
-(UIColor *)leftLineColor {
return objc_getAssociatedObject(self, &leftLineColorKey);
}
-(void)setLeftLineColor:(UIColor *)leftLineColor{
objc_setAssociatedObject(self, &leftLineColorKey,
leftLineColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(void)setLeftLineWidth:(CGFloat)leftLineWidth{
[self addLeftBorderWithColor:[self leftLineColor] andWidth:leftLineWidth];
}
- (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
dispatch_async(dispatch_get_main_queue(), ^{
CALayer *border = [CALayer layer];
border.name = @"topBorderLayer";
[self removePreviouslyAddedLayer:border.name];
border.backgroundColor = color.CGColor;
border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth);
[self.layer addSublayer:border];
[self addObserver:self forKeyPath: @"bounds" options:NSKeyValueObservingOptionNew context:topBorderContext];
});
}
- (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
dispatch_async(dispatch_get_main_queue(), ^{
CALayer *border = [CALayer layer];
border.name = @"bottomBorderLayer";
[self removePreviouslyAddedLayer:border.name];
border.backgroundColor = color.CGColor;
border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth);
[self.layer addSublayer:border];
[self addObserver:self forKeyPath: @"bounds" options:NSKeyValueObservingOptionNew context:bottomBorderContext];
});
}
- (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
dispatch_async(dispatch_get_main_queue(), ^{
CALayer *border = [CALayer layer];
border.name = @"leftBorderLayer";
[self removePreviouslyAddedLayer:border.name];
border.backgroundColor = color.CGColor;
border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height);
[self.layer addSublayer:border];
[self addObserver:self forKeyPath: @"bounds" options:NSKeyValueObservingOptionNew context:leftBorderContext];
});
}
- (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth {
dispatch_async(dispatch_get_main_queue(), ^{
CALayer *border = [CALayer layer];
border.name = @"rightBorderLayer";
[self removePreviouslyAddedLayer:border.name];
border.backgroundColor = color.CGColor;
border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height);
[self.layer addSublayer:border];
[self addObserver:self forKeyPath: @"bounds" options:NSKeyValueObservingOptionNew context:rightBorderContext];
});
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == topBorderContext) {
for (CALayer *border in self.layer.sublayers) {
if ([border.name isEqualToString:@"topBorderLayer"]) {
[border setFrame:CGRectMake(0, 0, self.frame.size.width, border.frame.size.height)];
}
}
} else if (context == bottomBorderContext) {
for (CALayer *border in self.layer.sublayers) {
if ([border.name isEqualToString:@"bottomBorderLayer"]) {
[border setFrame:CGRectMake(0, self.frame.size.height - border.frame.size.height, self.frame.size.width, border.frame.size.height)];
}
}
} else if (context == leftBorderContext) {
for (CALayer *border in self.layer.sublayers) {
if ([border.name isEqualToString:@"leftBorderLayer"]) {
[border setFrame:CGRectMake(0, 0, border.frame.size.width, self.frame.size.height)];
}
}
} else if (context == rightBorderContext) {
for (CALayer *border in self.layer.sublayers) {
if ([border.name isEqualToString:@"rightBorderLayer"]) {
[border setFrame:CGRectMake(self.frame.size.width - border.frame.size.width, 0, border.frame.size.width, self.frame.size.height)];
}
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)removePreviouslyAddedLayer:(NSString *)name {
if (self.layer.sublayers.count > 0) {
for (CALayer *layer in self.layer.sublayers) {
if ([layer.name isEqualToString:name]) {
[layer removeFromSuperlayer];
}
}
}
}
@end
Usage :-
Select any control from storyboard, then show attribute inspector (Right side) You will see below image Example.(Note : Border only appear at run time.)
Now You can set any side of border colour and width.