Problem when inserting a sublayer below an existing layer in Swift? - ios

I'm trying to add a sublayer behind an existing layer in Swift, but the sublayer is still coming up in front of the existing layer - I must have a problem in my code;
import UIKit
class ViewController: UIViewController {
let mainView = UIView()
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
let focusBG = UIView()
override func viewDidLoad() {
super.viewDidLoad()
mainView.backgroundColor = UIColor.gray
view.addSubview(mainView)
mainView.translatesAutoresizingMaskIntoConstraints = false
mainView.widthAnchor.constraint(equalToConstant: 325).isActive = true
mainView.heightAnchor.constraint(equalToConstant: ((325 / 9) * 16)).isActive = true
mainView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
mainView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
let tapView = UIView(frame: CGRect(x: 50, y: 0, width: 150, height: 200))
tapView.backgroundColor = UIColor.red
mainView.addSubview(tapView)
focusBG.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
focusBG.backgroundColor = UIColor(hue: 0.0, saturation: 0.0, brightness: 0.0, alpha: 0.4)
self.view.layer.insertSublayer(focusBG.layer, below: tapView.layer)
}
}
Ideally the red box would be in front of the greyed out layer - but this is what i am getting;
Thanks!
Update: Working Code
self.mainView.insertSubview(focusBG, belowSubview: tapView)
focusBG.translatesAutoresizingMaskIntoConstraints = false
focusBG.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
focusBG.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
focusBG.widthAnchor.constraint(equalToConstant: screenWidth).isActive = true
focusBG.heightAnchor.constraint(equalToConstant: screenHeight).isActive = true

You can try
self.mainView.insertSubview(focusBG, belowSubiew: tapView)
Or
self.mainView.layer.insertSublayer(focusBG.layer, below: tapView.layer) // not tested

Try using insertSublayer, method works for me.
self.mainView.layer.insertSublayer(your_Layer, at: 1) // 1 is the position of the layer you want to add to the mainView.
Thanks and let me know if this works for you. Dont Forget to upvote if it does ;)

Related

How to add shadow only to top and bottom to UIVIEW in swift

I am new in swift and I am trying to add shadow to UIView.
My code is like this
ViewPay.layer.masksToBounds = false
ViewPay.layer.shadowRadius = 2
ViewPay.layer.shadowOpacity = 1
ViewPay.layer.shadowColor = UIColor.red.cgColor
ViewPay.layer.shadowOffset = CGSize(width: 0 , height:2)
but it is adding shadow to all side
How can I add shadow like this
One solution is, put your ViewPay inside a container UIView.
Set your ViewPay to leading and trailing edges of container view, and for top and bottom add some padding to show shadow.
Also set container view clipsToBounds equals to true.
I hope to help with the solution of your question. In case you wanted something simple to understand, and also following the tips. You create a large container and add the required views. A vertical view of the purple color, another horizontal that would be gray color and in the middle put an image, as I did in the code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Big container
let container = UIView(frame: CGRect(x: self.view.bounds.width * 0.25, y: self.view.bounds.height * 0.5, width: 250, height: 290))
container.backgroundColor = .clear
self.view.addSubview(container)
//Purple view
let viewContainer = UIView(frame: CGRect(x: container.bounds.midX , y: 0, width: container.bounds.width * 0.89, height: container.bounds.height))
viewContainer.layer.anchorPoint.x = 1.0
viewContainer.backgroundColor = UIColor(displayP3Red: 158/255, green: 131/255, blue: 178/255, alpha: 1.0)
viewContainer.layer.cornerRadius = 8
viewContainer.clipsToBounds = true
container.addSubview(viewContainer)
//Gray view
let viewContainer2 = UIView(frame: CGRect(x: container.bounds.midX , y: container.bounds.midY, width: container.bounds.width * 0.93, height: container.bounds.height * 0.86))
viewContainer2.layer.anchorPoint.y = 1.0
viewContainer2.layer.anchorPoint.x = 1.0
viewContainer2.backgroundColor = UIColor(white: 0.5, alpha: 0.3)
viewContainer2.layer.cornerRadius = 5
viewContainer2.clipsToBounds = true
container.addSubview(viewContainer2)
//image
let imageView = UIImageView(frame: CGRect(x: container.bounds.midX, y: container.bounds.midY, width: container.bounds.width * 0.90, height: container.bounds.height * 0.90))
imageView.contentMode = .scaleToFill
imageView.layer.anchorPoint = CGPoint(x: 1.0, y: 1.0)
imageView.layer.cornerRadius = 10
imageView.clipsToBounds = true
imageView.image = UIImage(named: "image name")
container.addSubview(imageView)
}
}

Scale down UIView and it's subview simultaneously in swift 5?

I have created UIView and it has one subview and it's containing shape object which are creating using UIBezierPath, and UIView has fixed height and width (image 1). When i click the blue color button, it should be scale down to another fixed width and height. I have applied CGAffineTransform to subview and, i guess it is scale down properly, but since topAnchor and leftAchor has constant values, after scale down it is not displaying properly (image 2).I will attach my source code here and screenshots, please, analyze this and can someone suggest better approach for how to do this ? and highly appreciate ur feedback and comments.
code:
import UIKit
class ViewController: UIViewController {
var screenView: UIView!
var button: UIButton!
let widthOfScaleDownView: CGFloat = 200
let heightOfScaleDownView: CGFloat = 300
override func viewDidLoad() {
screenView = UIView(frame: .zero)
screenView.translatesAutoresizingMaskIntoConstraints = false
screenView.backgroundColor = UIColor.red
self.view.addSubview(screenView)
NSLayoutConstraint.activate([
screenView.heightAnchor.constraint(equalToConstant: 800),
screenView.widthAnchor.constraint(equalToConstant: 600),
screenView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0),
screenView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0)
])
button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.blue
self.view.addSubview(button)
NSLayoutConstraint.activate([
button.heightAnchor.constraint(equalToConstant: 50),
button.widthAnchor.constraint(equalToConstant: 100),
button.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 950),
button.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 350)
])
button.setTitle("click", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(scaleDownView(_:)), for: .touchUpInside)
let shapeView = UIView(frame: .zero)
shapeView.translatesAutoresizingMaskIntoConstraints = false
let rect = CGRect(x: 0, y: 0, width: 300, height: 300)
let shape = UIBezierPath(roundedRect: rect, cornerRadius: 50)
let layer = CAShapeLayer()
layer.path = shape.cgPath
layer.lineWidth = 2
shapeView.layer.addSublayer(layer)
screenView.addSubview(shapeView)
NSLayoutConstraint.activate([
shapeView.topAnchor.constraint(equalTo: screenView.topAnchor, constant: 250),
shapeView.leftAnchor.constraint(equalTo: screenView.leftAnchor, constant: 150)
])
}
#IBAction func scaleDownView(_ sender: UIButton) {
let widthScale = widthOfScaleDownView/screenView.frame.width
let heightScale = heightOfScaleDownView/screenView.frame.height
screenView.subviews.forEach{ view in
view.transform = CGAffineTransform(scaleX: widthScale, y: heightScale)
}
NSLayoutConstraint.activate([
screenView.heightAnchor.constraint(equalToConstant: heightOfScaleDownView),
screenView.widthAnchor.constraint(equalToConstant: widthOfScaleDownView),
screenView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0),
screenView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0)
])
}
}
Couple things...
1 - When you use CGAffineTransform to change a view, it will automatically affect the view's subviews. So no need for a loop.
2 - Transforms are cumulative, so when "going back" to the original scale, use .identity instead of another scale transform.
3 - You cannot set a constraint multiple times. So if you do:
screenView.heightAnchor.constraint(equalToConstant: 800)
and then you also do:
screenView.heightAnchor.constraint(equalToConstant: heightOfScaleDownView)
you will get auto-layout conflicts... the view cannot be both 800-pts tall and
300-pts tall at the same time.
Take a look at the changes I made to your code. I also added a 1-second animation to the scale transform so you can see what it's doing:
class ViewController: UIViewController {
var screenView: UIView!
var button: UIButton!
let widthOfScaleDownView: CGFloat = 200
let heightOfScaleDownView: CGFloat = 300
override func viewDidLoad() {
screenView = UIView(frame: .zero)
screenView.translatesAutoresizingMaskIntoConstraints = false
screenView.backgroundColor = UIColor.red
self.view.addSubview(screenView)
// constrain screenView
// width: 800 height: 600
// centered X and Y
NSLayoutConstraint.activate([
screenView.heightAnchor.constraint(equalToConstant: 800),
screenView.widthAnchor.constraint(equalToConstant: 600),
screenView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0),
screenView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0)
])
button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.blue
self.view.addSubview(button)
NSLayoutConstraint.activate([
button.heightAnchor.constraint(equalToConstant: 50),
button.widthAnchor.constraint(equalToConstant: 100),
button.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 950),
button.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 350)
])
button.setTitle("click", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(scaleDownView(_:)), for: .touchUpInside)
let shapeView = UIView(frame: .zero)
shapeView.translatesAutoresizingMaskIntoConstraints = false
let rect = CGRect(x: 0, y: 0, width: 300, height: 300)
let shape = UIBezierPath(roundedRect: rect, cornerRadius: 50)
let layer = CAShapeLayer()
layer.path = shape.cgPath
layer.lineWidth = 2
shapeView.layer.addSublayer(layer)
screenView.addSubview(shapeView)
// constrain shapeView
// width: 300 height: 300
// centered X and Y in screenView
NSLayoutConstraint.activate([
shapeView.widthAnchor.constraint(equalToConstant: 300.0),
shapeView.heightAnchor.constraint(equalToConstant: 300.0),
shapeView.centerXAnchor.constraint(equalTo: screenView.centerXAnchor),
shapeView.centerYAnchor.constraint(equalTo: screenView.centerYAnchor),
])
}
#IBAction func scaleDownView(_ sender: UIButton) {
let widthScale = widthOfScaleDownView/screenView.frame.width
let heightScale = heightOfScaleDownView/screenView.frame.height
UIView.animate(withDuration: 1.0) {
// if we have already been scaled down
if widthScale == 1 {
// animate the scale transform back to original (don't add another transform)
self.screenView.transform = .identity
} else {
// animate the scale-down transform
self.screenView.transform = CGAffineTransform(scaleX: widthScale, y: heightScale)
}
}
// do NOT change screenView constraints!
}
}

Bottom border for text field doesn’t appear

I tried to add a bottom border to my text field but it doesn't work. I know that there are similar questions out there but none of those answers help. Also, I removed this line of code below and tried because there is no border by default, it still doesn't work:
username.borderStyle = UITextField.BorderStyle.none
Here is the whole code:
class SignUpScreen: UIViewController {
let username = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(username)
// Background color
view.backgroundColor = .white
username.placeholder = "Name"
username.borderStyle = UITextField.BorderStyle.none
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0, y: view.frame.height - 1, width: view.frame.width, height: 1.0)
bottomLine.backgroundColor = UIColor.black.cgColor
username.layer.addSublayer(bottomLine)
username.translatesAutoresizingMaskIntoConstraints = false
username.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
username.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
username.widthAnchor.constraint(equalToConstant: 150).isActive = true
username.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
}
Hope someone can help me out! Thanks! :)
Here is what you were missing: You were using the main view's frame for creating the frame of your layer. You must use the text field.
Also first draw your text field properly with constraints then add layer to text field.
In viewDidLoad your content view is loaded to main memory. There is no frame assignment in that method. So use viewWillAppear or viewDidAppear.
Now when your view will appear every time a new TextField & a layer will be added. So you need to make a check to restrict that behaviour. E.g if a text field is already added to your view then don't add.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
view.addSubview(username)
/*
* Create the text field
*/
view.backgroundColor = .white
username.placeholder = "Name"
username.borderStyle = UITextField.BorderStyle.none
username.translatesAutoresizingMaskIntoConstraints = false
username.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
username.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
username.widthAnchor.constraint(equalToConstant: 300).isActive = true
username.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
/*
* Here add the shap layer
*/
let bottomLine = CALayer()
/*
* Here is what you were missing.
* You were using the main view's frame instead of your textfield
*/
bottomLine.frame = CGRect(x: 0, y: username.bounds.height - 1, width: username.bounds.width, height: 1.0)
bottomLine.backgroundColor = UIColor.black.cgColor
username.layer.addSublayer(bottomLine)
}
bottomLine.frame = CGRect(x: 0, y: view.frame.height - 1, width: view.frame.width, height: 1.0)
you must use username.bounds, instead of view.frame
let border = CALayer()
let width = CGFloat(3.0)
border.borderColor = UIColor.black.cgColor
border.frame = CGRect(x: 0, y: textFieldOutlet.frame.size.height - width, width: textFieldOutlet.frame.size.width, height: textFieldOutlet.frame.size.height)
border.borderWidth = width
textFieldOutlet.layer.addSublayer(border)
textFieldOutlet.layer.masksToBounds = true
rather than adding a layer maybe you should add your border as UIView and bring it to top also set your textField bgColor as clearColor:
class SignUpScreen: UIViewController {
let username = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(username)
// Background color
view.backgroundColor = .white
username.placeholder = "Name"
username.borderStyle = UITextField.BorderStyle.none
// make sure your textField doesn't have back ground
username.backgroundColor = .clear
let bottomLine = UIView()
bottomLine.frame = CGRect(x: 0, y: view.frame.height - 1, width: view.frame.width, height: 1.0)
bottomLine.backgroundColor = UIColor.black.cgColor
// be sure you bring it to top
self.view.layer.insertSublayer(bottomLine, at:0)
username.translatesAutoresizingMaskIntoConstraints = false
username.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
username.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
username.widthAnchor.constraint(equalToConstant: 150).isActive = true
username.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
}
Try this
func bottomBorderTextField(_ textField: UIView, color: UIColor) {
// For Bottom Border
let bottomLayer = CALayer()
let frame = viewType.frame
bottomLayer.frame = CGRect(x: 0, y: frame.height - 1, width: frame.width - 2, height: 0.5)
bottomLayer.backgroundColor = color.cgColor
viewType.layer.addSublayer(bottomLayer)
}
In viewDidload()
self.bottomBorderTextField(self.txtField, color: yourTextColor)

Activity Indicator not appears center on custom UIAlertView when search view controller push ups VC

I want to show the activity indicator on UIAlertViewController which contain the table view, So its working perfectly, but second case is when search controller appears on the Parent of UIAlertViewController then activity indicator not appears in center on UIAlertViewController.
Attaching image before attaching searchVC on Parent
And when searchVC added on Parent then
Following is code of activity indicator
var actInd: UIActivityIndicatorView = UIActivityIndicatorView()
func startActivityIndicator() {
self.view.isUserInteractionEnabled = false
let loadingView: UIView = UIView()
loadingView.frame = CGRect(x: 0, y: 0, width: 80.0, height: 80.0)
loadingView.center = self.view.center
loadingView.backgroundColor = UIColor(red: 44/255, green: 44/255, blue: 44/255, alpha: 0.7)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
actInd.frame = CGRect(x: 0, y: 0, width: 40.0, height: 40.0)
actInd.style = .whiteLarge
actInd.center = CGPoint(x: loadingView.frame.size.width / 2, y: loadingView.frame.size.height / 2)
loadingView.addSubview(actInd)
self.view.addSubview(loadingView)
actInd.startAnimating()
}
func stopActivityIndicator() {
self.view.isUserInteractionEnabled = true
actInd.stopAnimating()
let view = actInd.superview
view?.removeFromSuperview()
}
above code I have added in extension on UIViewController
Finally solved it by setting constraints by following way
func startActivityIndicator() {
self.view.isUserInteractionEnabled = false
let loadingView: UIView = UIView()
loadingView.translatesAutoresizingMaskIntoConstraints = false
actInd.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(loadingView)
loadingView.addSubview(actInd)
loadingView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
loadingView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
loadingView.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
loadingView.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
loadingView.center = self.view.center
loadingView.backgroundColor = UIColor(red: 44/255, green: 44/255, blue: 44/255, alpha: 0.7)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
actInd.leadingAnchor.constraint(equalTo: loadingView.leadingAnchor).isActive = true
actInd.trailingAnchor.constraint(equalTo: loadingView.trailingAnchor).isActive = true
actInd.topAnchor.constraint(equalTo: loadingView.topAnchor).isActive = true
actInd.bottomAnchor.constraint(equalTo: loadingView.bottomAnchor).isActive = true
actInd.style = .whiteLarge
actInd.center = CGPoint(x: loadingView.frame.size.width / 2, y: loadingView.frame.size.height / 2)
actInd.startAnimating()
}

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