Circular UIView Not A Full Circle - uiview

I have an interesting issue, but I'm not sure how to solve it. I am attempting to extend UIView with an #IBInspectable. However, with this method the corner radius seems to be set from the nib files default side, not the actual size of the view.
So, when in IB I set the "View as" to iPhoneSE and build for iPhoneSE, the view is a circle. However, if I build for iPhone7, the corners are not fully rounded into a circle. Conversely, if I set "View as" to iPhone7 and build for iPhone7, the view is a circle, However, if I build for iPhoneSE the corners are over-rounded.
Pictures and code below:
Extension
extension UIView {
#IBInspectable var cornerRadius:Double {
get {
return Double(layer.cornerRadius)
}
set {
layer.cornerRadius = CGFloat(newValue)
layer.masksToBounds = newValue > 0
}
}
#IBInspectable var circleView:Bool {
get {
return layer.cornerRadius == min(self.frame.width, self.frame.height) / CGFloat(2.0) ? true : false
}
set {
if newValue {
layer.cornerRadius = min(self.frame.width, self.frame.height) / CGFloat(2.0)
layer.masksToBounds = true
}
else{
layer.cornerRadius = 0.0
layer.masksToBounds = false
}
}
}
}
"View as" set as iPhoneSE in IB
Built for iPhoneSE
Build for iPhone 7
"View as" set as iPhone7
Build for iPhone SE
Build for iPhone 7
IB Settings

You will only get a full circle if your view is a square.
If you're view is a rectangle and you set the cornerRadius with a value less than half the smallest dimension, you will get the second view and if it's a value greater than that you will get the diamond shaped one.
Check your constraints and you should compute the cornerRadius after the view finishes layout in viewDidLayout so you get the correct size.
Here is a Playground showing this (I've added an animation to show better the issue):
import UIKit
import PlaygroundSupport
extension UIView
{
func addCornerRadiusAnimation(from: CGFloat, to: CGFloat, duration: CFTimeInterval)
{
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = from
animation.toValue = to
animation.duration = duration
self.layer.add(animation, forKey: "cornerRadius")
self.layer.cornerRadius = to
}
}
let v = UIView(frame: CGRect(x: 0, y: 0, width: 350, height: 450))
PlaygroundPage.current.liveView = v
let squareView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
squareView.backgroundColor = .red
squareView.layer.masksToBounds = true
v.addSubview(squareView)
var cornerRadius = CGFloat(0.5*min(squareView.frame.width,
squareView.frame.height))
squareView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)
let rectangleView = UIView(frame: CGRect(x: 10, y: 200, width: 100, height: 200))
rectangleView.backgroundColor = .blue
rectangleView.layer.masksToBounds = true
v.addSubview(rectangleView)
cornerRadius = CGFloat(0.5*min(rectangleView.frame.width,
rectangleView.frame.height))
rectangleView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)
let diamondView = UIView(frame: CGRect(x: 200, y: 200, width: 100, height: 200))
diamondView.backgroundColor = .green
diamondView.layer.masksToBounds = true
v.addSubview(diamondView)
cornerRadius = CGFloat(0.5*max(diamondView.frame.width,
diamondView.frame.height))
diamondView.addCornerRadiusAnimation(from: 0, to: cornerRadius, duration: 5)

You mentioned Width and Height as 185.5 but you kept width constraint as 350.

If you don't want to move on viewDidLayout you can simply made the trick before setting your corner radius by adding .layoutIfNeeded().
yourCircleView.layoutIfNeeded()
yourCircleView.layer.cornerRadius = yourCircleView.frame.size.width/2

If still failing, in Xcode remove all (ALL) restraints and add the following in the Cell Class:
override func awakeFromNib() {
super.awakeFromNib()
userImageView.layoutIfNeeded()
userImageView.layer.cornerRadius = userImageView.frame.size.width / 2
userImageView.layer.masksToBounds = true
userImageView.clipsToBounds = true
}

Related

space at the beginning of my view's border

am adding a border to my view like this:
func addBottomBorderWithColor(color: UIColor, width: CGFloat) {
let border = CALayer()
border.backgroundColor = color.cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height, width: self.frame.size.width, height: width)
self.layer.addSublayer(border)
}
but strangley this works fine on Iphone 7 but in iphone 7 plus .. i will get this result:
as you can see there's a space at the beginning of the view's border.. why?
same thing is happening with view's shadow .. am doing it like this:
func dropShadow(scale: Bool = true) {
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize.zero
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
and getting space at the end of the view on iphone 7 plus
how to solve it?
it means your UI Is not updated , call in viewdidAppear or force to update the UI in mainthread on viewdidload using
DispatchQueue.main.async {
//call your addBottomBorderWithColor
}
Try calling the addBottomBorderWithColor in:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
The UI gets updated according to the constraints after ViewDidLoad.

IOS - How to create Facebook reaction bar with blur background?

Although it may not be the week to replicate some design of Facebook, I would like to be able to design my own version of the reaction indicator view below.
I have three UIImageViews lined in the same positions as above. The problem is that, unlike Facebook, the background color may change (i.e is on top of a UIBlurEffect) and therefore I am unable to set the border color to white.
I thought it would make sense to set the borderColor like so:
imageViewOne.layer.borderColor = UIColor.clear.cgColor
imageViewOne.layer.borderWidth = 2
However, the underlying imageViewTwo is displayed in the border instead of the background color.
So far, I have this:
Would appreciate some help/ideas on how to make this work - I'm thinking of masks but not sure whether a. this is the correct solution and b. how to achieve the desired effect. To clarify, I am not able to set the border color as a constant as it will change with the UIBlurEffect.
In my opinion, there are 2 way to resolve your problem.
Create and use clipped image for Wow and Love like below Love image.
Another way is using mask property of UIView. Creating a mask image and apply it for mask property.
Mask image looks like.
Code for applying mask.
let imvLoveMask = UIImageView.init(image: UIImage.init(named: "MASK_IMAGE_NAME"));
imvLoveMask.frame = CGRect.init(x: 0, y: 0, width: imvLove.frame.size.width, height: imvLove.frame.size.height);
imvLove.mask = imvLoveMask;
Both of 2 above way can help you achieve what you want in the question. Background of icons in below image is an UIVisualEffectView.
In my opinion, the first way with clipped image is better and faster because you don't need to apply mask for your imageView. But if you don't want to create a clipped image for some reason, you can use the second way.
For more detail, you can take a look at my demo repo
You need to clip part of the image in order to let underlying content be visible in the gaps between images. See playground sample.
Add smile_1, smile_2, smile_3 images to playground resources. I took emoji images from https://emojipedia.org/facebook/.
import UIKit
import PlaygroundSupport
class EmojiView: UIView {
var imageView = UIImageView()
var imageInset = UIEdgeInsets(top: 3.0, left: 3.0, bottom: 3.0, right: 3.0)
var shapeLayer = CAShapeLayer()
var overlap: CGFloat = 0.0 {
didSet {
self.updateShape()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: UIView
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.imageView.frame = UIEdgeInsetsInsetRect(self.bounds, self.imageInset)
self.shapeLayer.frame = self.bounds
self.updateShape()
}
// MARK: Private
private func setup() {
self.addSubview(self.imageView)
self.layer.mask = self.shapeLayer
}
private func updateShape() {
let frame = self.bounds
let path = UIBezierPath(rect: frame)
// Cut off part of the image if overlap more then > 0
if 0 < self.overlap {
path.usesEvenOddFillRule = true
path.append(UIBezierPath(ovalIn: CGRect(x: -frame.width + self.overlap, y: 0.0, width: frame.width, height: frame.height)).reversing())
}
self.shapeLayer.path = path.cgPath
}
}
let overlap: CGFloat = 10 // Amount of pixels emojis overlap each other
// Create emoji views
let emojiView_1 = EmojiView(frame: CGRect(x: 5.0, y: 5.0, width: 40.0, height: 40.0))
emojiView_1.imageView.image = UIImage(named: "smile_1")
let emojiView_2 = EmojiView(frame: CGRect(x: emojiView_1.frame.maxX - overlap, y: 5.0, width: 40.0, height: 40.0))
emojiView_2.imageView.image = UIImage(named: "smile_2")
emojiView_2.overlap = overlap
let emojiView_3 = EmojiView(frame: CGRect(x: emojiView_2.frame.maxX - overlap, y: 5.0, width: 40.0, height: 40.0))
emojiView_3.imageView.image = UIImage(named: "smile_3")
emojiView_3.overlap = overlap
let holderView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: emojiView_3.frame.maxX + 5, height: 50.0))
// Add gradient layer
let gradientLayer = CAGradientLayer()
gradientLayer.frame = holderView.bounds
gradientLayer.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
holderView.layer.addSublayer(gradientLayer)
// Add emoji views
holderView.addSubview(emojiView_1)
holderView.addSubview(emojiView_2)
holderView.addSubview(emojiView_3)
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = holderView
use this :
self.imageViewOne.layer.cornerRadius = self.imageViewOne.layer.bounds.width/2
self.imageViewOne.layer.masksToBounds = true
Simple suggestion: As you are setting border color programatically, you have a control to change it, according to background color (if background color is solid (not a gradient)).
imageViewOne.layer.borderColor = imageViewOne.superview?.backgroundColor ?? UIColor.white
imageViewOne.layer.borderWidth = 2.0
Actually instead of masking, you can put your images in a view which has white background and round(set corner radius). Then you can put these views (which has white background and images in it) via settings their zPosition or on storyboard with view hierarchy.
I've prepared a little playground for you. You can see the result in the screenshot. I've put a view inside the containerViews instead you can use uiimageview etc. It's a bit ugly but solves your issue I guess it's up to you to decide how use it.
Here is the code, you can just copy and paste it to a new playground and test it.
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var mainContainerView = UIView(frame: CGRect(x: 0, y: 0, width: 140, height: 80))
mainContainerView.backgroundColor = UIColor.white
var containerView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
containerView.backgroundColor = UIColor.white
containerView.layer.cornerRadius = containerView.frame.width / 2
var innerView = UIView(frame: CGRect(x: 10, y: 10, width: 60, height: 60))
innerView.backgroundColor = UIColor.red
innerView.layer.cornerRadius = innerView.frame.width / 2
containerView.addSubview(innerView)
var containerView2 = UIView(frame: CGRect(x: 60, y: 0, width: 80, height: 80))
containerView2.backgroundColor = UIColor.yellow
containerView2.layer.cornerRadius = containerView2.frame.width / 2
var innerView2 = UIView(frame: CGRect(x: 10, y: 10, width: 60, height: 60))
innerView2.backgroundColor = UIColor.green
innerView2.layer.cornerRadius = innerView2.frame.width / 2
containerView2.addSubview(innerView2)
containerView.layer.zPosition = 2
containerView2.layer.zPosition = 1
mainContainerView.addSubview(containerView)
mainContainerView.addSubview(containerView2)
PlaygroundPage.current.liveView = mainContainerView

Unable to set bottom shadow of UIView in iOS?

I want to set the bottom shadow of UIView.I am also setting corner radius.If i set maskToBounds to true then i am not able to set the shadow of UIView please tell how can i set both corner radius & shadow of UIView.
func addShadwToView(){
self.viewContainer.layer.masksToBounds = true;
self.viewContainer.layer.shadowRadius = 15;
self.viewContainer.layer.shadowOffset = CGSize(width: 0, height: 20)
self.viewContainer.layer.shadowOpacity = 0.5;
self.viewContainer.layer.cornerRadius = 5
self.viewContainer.layer.borderColor = UIColor.lightGray.cgColor
self.viewContainer.layer.borderWidth = 0.5
}
If you want both a corner radius and a drop shadow, so don't turn on -masksToBounds, but rather set the corner radius and set the bezier path of the shadow with a rounded rect. Keep the radius of the two the same:
Try it:
func addShadwToView(){
var borderLine = CAShapeLayer()
borderLine.path = UIBezierPath(roundedRect: frame2, byRoundingCorners: [.allCorners], cornerRadii: CGSize(width: 30, height: 0)).cgPath
borderLine.shadowColor = UIColor.white.cgColor
borderLine.shadowOffset = CGSize(width: 0, height: 1)
borderLine.shadowOpacity = 0.3
borderLine.shadowRadius = 10
self.viewContainer.layer.masksToBounds = true;
self.viewContainer.layer.cornerRadius = 5
self.viewContainer.layer.borderColor = UIColor.lightGray.cgColor
self.viewContainer.layer.borderWidth = 0.5
self.viewContainer.layer.addSublayer(borderLine)
}
Try this code...this will solve your problem
func addShadow(){
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: customView.frame.width, height: customView.frame.height))
customView.layer.shadowColor = UIColor.lightGray.cgColor
customView.layer.shadowOffset = CGSize(width: 0, height: 20)
customView.layer.shadowOpacity = 0.5
customView.layer.shadowRadius = 15
customView.layer.masksToBounds = false
customView.layer.shadowPath = shadowPath.cgPath
}

Set Shadow on Bottom UIView only

I want to create bottom only shadow on UIView. Right now with this function, will create shadow in top, bottom, left, and right.
func setCardView(view : UIView){
view.layer.masksToBounds = false
view.layer.shadowOffset = CGSize(width: 0, height: 0)
view.layer.shadowRadius = 2
view.layer.shadowOpacity = 0.5
}
Is there anyway to only create shadow in the bottom?
Any help would be appreciated. Thank you!
I think the proper way of thinking of shadow is, the shadow belongs to the object, which is the button, the uiview, not just part of the side. Imagining there is a virtual light source. You can't really just create a shadow for one side.
With that being said, the shadow will always be the shadow of the view as a whole. However, you can change the shadow offset to make it towards to the bottom.
view.layer.shadowOffset = CGSize(width: 0, height: 3)
This means you want the light source shoot the light from top to make the shadow to the bottom. The reason you still see some shadow at the top is the shadow radius. which is to simulate the diffuse of the light. The more diffuse the light is, the softer the shadow will be so you will see top shadow still.
view.layer.shadowRadius = 1 or 0.5
try to reduce the radius also. it will give you a better visual result.
To understand umbra, penumbra and antumbra if you need, check out https://en.wikipedia.org/wiki/Umbra,_penumbra_and_antumbra
This adds a shadow only to bottom. Implemented as an extension to UIView
extension UIView {
func addBottomShadow() {
layer.masksToBounds = false
layer.shadowRadius = 4
layer.shadowOpacity = 1
layer.shadowColor = UIColor.gray.cgColor
layer.shadowOffset = CGSize(width: 0 , height: 2)
layer.shadowPath = UIBezierPath(rect: CGRect(x: 0,
y: bounds.maxY - layer.shadowRadius,
width: bounds.width,
height: layer.shadowRadius)).cgPath
}
}
This code working for swift 4 and shadow applying for view Bottom:
view.layer.masksToBounds = false
view.layer.shadowRadius = 4
view.layer.shadowOpacity = 1
view.layer.shadowColor = UIColor.gray.cgColor
view.layer.shadowOffset = CGSize(width: 0 , height:2)
If you really want a shadow only on one side of your UIView, you should set your view.layer.shadowPath to a UIBezierPath.
Here is an example which will only display a shadow at the bottom of the view:
view.layer.shadowPath = UIBezierPath(rect: CGRect(x: 0,
y: bounds.maxY - layer.shadowRadius,
width: bounds.width,
height: layer.shadowRadius)).cgPath
Deconstructing the CGRect value, you get:
x and width make sure the shadow takes the full horizontal width of your view (you might want to adjust them, for example using the layer.shadowRadius value as a basis for your offsetting)
y and height make sure the shadow starts as low as possible and then is only as big as the radius
Of course, there are some cases where this won't work, for example when you want a shadowRadius larger than your view's height. In these cases, I would recommend using an image view or a masked layer.
Hope this helps,
Change your shadowOffset
view.layer.shadowOffset = CGSize(width: 0, height: 3)
Here's the proper way of applying shadow in Swift:
yourView.layer.shadowOffset = CGSize(width: 0, height: 3)
yourView.layer.shadowOpacity = 0.6
yourView.layer.shadowRadius = 3.0
yourView.layer.shadowColor = UIColor.red.cgColor
Old question but it seems none of the answers really answer the question.
While the above answers will work with a low shadowRadius you don't really get a 'shadow' effect.
You can absolutely add a shadow to just the bottom by using UIBezierPath
Heres how -
let shadowWidth: CGFloat = 1.2 // Shadow width, will be the width furthest away from the view, this is equivalent to 120% of the views width
let shadowHeight: CGFloat = 0.3 // Shadow height, again this is equivalent to 30%
let shadowRadius: CGFloat = 5
let width = someView.frame.width
let height = someView.frame.height // Get width and height of the view
// Plot the path
let shadowPath = UIBezierPath()
shadowPath.move(to: CGPoint(x: shadowRadius / 2, y: height - shadowRadius / 2))
shadowPath.addLine(to: CGPoint(x: width - shadowRadius / 2, y: height - shadowRadius / 2))
shadowPath.addLine(to: CGPoint(x: width * shadowWidth, y: height + (height * shadowHeight)))
shadowPath.addLine(to: CGPoint(x: width * -(shadowWidth - 1), y: height + (height * shadowHeight)))
// Add shadow
someView.layer.shadowPath = shadowPath.cgPath
someView.layer.shadowRadius = shadowRadius
someView.layer.shadowOffset = .zero
someView.layer.shadowOpacity = 0.2
This outputs this
Or if you wanted a simpler solution, with less options you could go with this
let buttonHeight = someButton.frame.height
let buttonWidth = someButton.frame.width
let shadowSize: CGFloat = 15
let contactRect = CGRect(x: -shadowSize, y: buttonHeight - (shadowSize * 0.2), width: buttonWidth + shadowSize * 2, height: shadowSize)
someButton.layer.shadowPath = UIBezierPath(ovalIn: contactRect).cgPath
someButton.layer.shadowRadius = 5
someButton.layer.shadowOpacity = 0.6
Which will output this
Example here
https://github.com/hemo87/ExampleShadow/tree/master
class ViewBottomShadow: UIView {
init() {
super.init(frame: .zero)
backgroundColor = .white
layer.masksToBounds = false
layer.shadowRadius = 2
layer.shadowOpacity = 1
layer.shadowColor = UIColor.gray.cgColor
layer.shadowOffset = CGSize(width: 0 , height:2)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Just draw regular shadow and rotate it upside down, as simple as that
#objc func shadowView() -> UIView {
let shadowView = UIView(frame: .zero)
shadowView.backgroundColor = .white
shadowView.layer.shadowColor = UIColor.grey.cgColor
shadowView.layer.shadowOffset = CGSize(width: 0, height: 2)
shadowView.layer.shadowOpacity = 1.0
shadowView.layer.shadowRadius = 4
shadowView.layer.compositingFilter = "multiplyBlendMode"
return shadowView
}
func idtm_addBottomShadow() {
let shadow = shadowView()
shadow.transform = transform.rotated(by: 180 * CGFloat(Double.pi))
shadow.transform = transform.rotated(by: -1 * CGFloat(Double.pi))
shadow.translatesAutoresizingMaskIntoConstraints = false
addSubview(shadow)
NSLayoutConstraint.activate([
shadow.leadingAnchor.constraint(equalTo: leadingAnchor),
shadow.trailingAnchor.constraint(equalTo: trailingAnchor),
shadow.bottomAnchor.constraint(equalTo: bottomAnchor),
shadow.heightAnchor.constraint(equalToConstant: 1),
])
}

How to only show bottom border of UITextField in Swift

I want to show only bottom border and hide the other sides.
Output I see: As you can see I see the top, left and right borders also and they are black in color, I want to remove them. Only need the bottom white thick 2.0 border.
Code I am using (source):
var border = CALayer()
var width = CGFloat(2.0)
border.borderColor = UIColor.whiteColor().CGColor
border.frame = CGRect(x: 0, y: tv_username.frame.size.height - width, width: tv_username.frame.size.width, height: tv_username.frame.size.height)
border.borderWidth = width
tv_username.backgroundColor = UIColor.clearColor()
tv_username.layer.addSublayer(border)
tv_username.layer.masksToBounds = true
tv_username.textColor = UIColor.whiteColor()
Try to do by this way, with Swift 5.1:
var bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0.0, y: myTextField.frame.height - 1, width: myTextField.frame.width, height: 1.0)
bottomLine.backgroundColor = UIColor.white.cgColor
myTextField.borderStyle = UITextField.BorderStyle.none
myTextField.layer.addSublayer(bottomLine)
You have to set the borderStyle property to None
If you are using the autolayout then set perfect constraint else bottomline will not appear.
Hope it helps.
Thought from #Ashish's answer, used same approach long ago in Objective-C but implementing extension will be more useful.
extension UITextField {
func addBottomBorder(){
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0, y: self.frame.size.height - 1, width: self.frame.size.width, height: 1)
bottomLine.backgroundColor = UIColor.white.cgColor
borderStyle = .none
layer.addSublayer(bottomLine)
}
}
In your controller:
self.textField.addBottomBorder()
Can add further parameters to your method, like adding border height, color.
#mina-fawzy
I liked the answer that included masksToBounds by Mina Fawzy...
I ran into this issue where I was trying to style a bottom border of a UITextField, and the comments using a CGRect worked for me, however, I ran into issues when using different screen sizes, or if I changed the orientation to landscape view from the portrait.
ie. My Xcode Main.storyboard was designed with iPhone XS Max, with a UITextField constrained to be 20 points from the left/right of the screen. In my viewDidLoad() I stylized the UITextField (textfield) using the CGRect approach, making the width of the rectangle equal to textfield.frame.width.
When testing on the iPhone XS Max, everything worked perfectly, BUT, when I tested on iPhone 7 (smaller screen width) the CGRect was grabbing the width of the iPhone XS Max during the viewDidLoad(), causing the rectangle (bottom line) to be too wide, and the right edge went off the screen. Similarly, when I tested on iPad screens, the bottom line was way too short. And also, on any device, rotating to landscape view did not re-calculate the size of the rectangle needed for the bottom line.
The best solution I found was to set the width of the CGRect to larger than the longest iPad dimension (I randomly chose 2000) and THEN added textfield.layer.masksToBounds = true. This worked perfectly because now the line is plenty long from the beginning, does not need to be re-calculated ever, and is clipped to the correct width of the UITextField no matter what screen size or orientation.
Thanks Mina, and hope this helps others with the same issue!
Objective C
[txt.layer setBackgroundColor: [[UIColor whiteColor] CGColor]];
[txt.layer setBorderColor: [[UIColor grayColor] CGColor]];
[txt.layer setBorderWidth: 0.0];
[txt.layer setCornerRadius:12.0f];
[txt.layer setMasksToBounds:NO];
[txt.layer setShadowRadius:2.0f];
txt.layer.shadowColor = [[UIColor blackColor] CGColor];
txt.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
txt.layer.shadowOpacity = 1.0f;
txt.layer.shadowRadius = 1.0f;
Swift
textField.layer.backgroundColor = UIColor.whiteColor().CGColor
textField.layer.borderColor = UIColor.grayColor().CGColor
textField.layer.borderWidth = 0.0
textField.layer.cornerRadius = 5
textField.layer.masksToBounds = false
textField.layer.shadowRadius = 2.0
textField.layer.shadowColor = UIColor.blackColor().CGColor
textField.layer.shadowOffset = CGSizeMake(1.0, 1.0)
textField.layer.shadowOpacity = 1.0
textField.layer.shadowRadius = 1.0
I have tried all this answer but no one worked for me except this one
let borderWidth:CGFloat = 2.0 // what ever border width do you prefer
let bottomLine = CALayer()
bottomLine.frame = CGRectMake(0.0, Et_textfield.height - borderWidth, Et_textfield.width, Et_textfield.height )
bottomLine.backgroundColor = UIColor.blueColor().CGColor
bottomLine
Et_textfield.layer.addSublayer(bottomLine)
Et_textfield.layer.masksToBounds = true // the most important line of code
Swift 3:
Just subclass your UITextField
class BottomBorderTF: UITextField {
var bottomBorder = UIView()
override func awakeFromNib() {
//MARK: Setup Bottom-Border
self.translatesAutoresizingMaskIntoConstraints = false
bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
bottomBorder.backgroundColor = UIColor.orange
bottomBorder.translatesAutoresizingMaskIntoConstraints = false
addSubview(bottomBorder)
//Mark: Setup Anchors
bottomBorder.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
bottomBorder.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
bottomBorder.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
bottomBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true // Set Border-Strength
}
}
Solution which using CALayer is not good because when device is rotated the underline doesn't change width.
class UnderlinedTextField: UITextField {
override func awakeFromNib() {
super.awakeFromNib()
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0, y: self.frame.size.height, width: UIScreen.main.bounds.width, height: 1)
bottomLine.bounds = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: self.frame.size.height)
bottomLine.backgroundColor = UIColor.black.cgColor
borderStyle = .none
layer.addSublayer(bottomLine)
layer.masksToBounds = true
}
}
The best solution is to use UIView.
class UnderlinedTextField: UITextField {
private let defaultUnderlineColor = UIColor.black
private let bottomLine = UIView()
override func awakeFromNib() {
super.awakeFromNib()
borderStyle = .none
bottomLine.translatesAutoresizingMaskIntoConstraints = false
bottomLine.backgroundColor = defaultUnderlineColor
self.addSubview(bottomLine)
bottomLine.topAnchor.constraint(equalTo: self.bottomAnchor, constant: 1).isActive = true
bottomLine.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
bottomLine.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
bottomLine.heightAnchor.constraint(equalToConstant: 1).isActive = true
}
public func setUnderlineColor(color: UIColor = .red) {
bottomLine.backgroundColor = color
}
public func setDefaultUnderlineColor() {
bottomLine.backgroundColor = defaultUnderlineColor
}
}
First set borderStyle property to .none.
Also, don't forget that the best time to call this method in the viewDidAppear(_:) method.
To make it handy, you can use an extension:
extension UIView {
func addBottomBorderWithColor(color: UIColor, width: CGFloat) {
let border = CALayer()
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)
}
}
Call it like:
textfield.addBottomBorderWithColor(color: UIColor.lightGray, width: 0.5)
Using extension and Swift 5.3
extension UITextField {
internal func addBottomBorder(height: CGFloat = 1.0, color: UIColor = .black) {
let borderView = UIView()
borderView.backgroundColor = color
borderView.translatesAutoresizingMaskIntoConstraints = false
addSubview(borderView)
NSLayoutConstraint.activate(
[
borderView.leadingAnchor.constraint(equalTo: leadingAnchor),
borderView.trailingAnchor.constraint(equalTo: trailingAnchor),
borderView.bottomAnchor.constraint(equalTo: bottomAnchor),
borderView.heightAnchor.constraint(equalToConstant: height)
]
)
}
}
For those looking for a solution that works for Autolayout, IBInspectable, and the Storyboard, subclass UITextField into your custom textfield class and add these:
func setUnderline() {
for sub in self.subviews {
sub.removeFromSuperview()
}
if underlineStyle == true {
var bottomBorder = UIView()
bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
bottomBorder.backgroundColor = borderColor //YOUR UNDERLINE COLOR HERE
bottomBorder.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(bottomBorder)
bottomBorder.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
bottomBorder.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
bottomBorder.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
bottomBorder.heightAnchor.constraint(equalToConstant: underlineHeight).isActive = true
layoutIfNeeded()
}
}
#IBInspectable var underlineStyle: Bool = false {
didSet {
setUnderline()
}
}
#IBInspectable var underlineHeight: CGFloat = 0 {
didSet {
setUnderline()
}
}
Swift 5.
extension UITextField {
let bottomLine = UIView()
bottomLine.backgroundColor = .black
borderStyle = .none
self.addSubview(bottomLine)
NSLayoutConstraint.activate([
bottomLine.topAnchor.constraint(equalTo: bottomAnchor + 10),
bottomLine.leadingAnchor.constraint(equalTo: leadingAnchor),
bottomLine.trailingAnchor.constraint(equalTo: trailingAnchor),
bottomLine.heightAnchor.constraint(equalToConstant: 1)
])
}
For multiple Text Field
override func viewDidLoad() {
configureTextField(x: 0, y: locationField.frame.size.height-1.0, width: locationField.frame.size.width, height:1.0, textField: locationField)
configureTextField(x: 0, y: destinationField.frame.size.height-1.0, width: destinationField.frame.size.width, height:1.0, textField: destinationField)
configureTextField(x: 0, y: originField.frame.size.height-1.0, width: originField.frame.size.width, height:1.0, textField: originField)
configureTextField(x: 0, y: nameField.frame.size.height-1.0, width: nameField.frame.size.width, height:1.0, textField: nameField)
locationField.text="Hello"
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func configureTextField(x:CGFloat,y:CGFloat,width:CGFloat,height:CGFloat,textField:UITextField)
{
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: x, y: y, width: width, height: height)
bottomLine.backgroundColor = UIColor.white.cgColor
textField.borderStyle = UITextBorderStyle.none
textField.layer.addSublayer(bottomLine)
}
Textfield bottom border set but some more issues for devices.So bottom border not fit in textfield.I retrieve that problem the code like this
It works fine
swift 4.2
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0.0, y: textField.frame.height - 1, width: screenSize.width - 32, height: 1.0)
bottomLine.backgroundColor = UIColor(hex: 0xD5D5D5).cgColor
textField.borderStyle = UITextField.BorderStyle.none
textField.layer.addSublayer(bottomLine)
For swift 4. this works for me.
let myfield:UITextField = {
let mf=UITextField()
let atributePlaceHolder=NSAttributedString(string: "Text_description", attributes:[NSAttributedString.Key.foregroundColor :UIColor.darkGray])
mf.textColor = .gray
mf.attributedPlaceholder=atributePlaceHolder
mf.layer.borderColor = UIColor.black.cgColor
mf.layer.backgroundColor = UIColor.white.cgColor
mf.layer.borderWidth = 0.0
mf.layer.shadowOffset = CGSize(width: 0, height: 1.0)
mf.layer.shadowOpacity = 1.0
mf.layer.shadowRadius = 0.0
return mf
}()

Resources