I have a problem, I added secureTextEntry toggle in my text field, but text is on toggle button.
extension UITextField {
fileprivate func setPasswordToggleImage(_ button: UIButton) {
if(isSecureTextEntry){
button.setImage(UIImage(named: "eye-active"), for: .normal)
}else{
button.setImage(UIImage(named: "eye-inactive"), for: .normal)
}
}
func enablePasswordToggle(){
let button = UIButton(type: .custom)
setPasswordToggleImage(button)
button.frame = CGRect(x: CGFloat(self.frame.size.width - 25), y: CGFloat(5), width: CGFloat(25), height: CGFloat(25))
button.addTarget(self, action: #selector(self.togglePasswordView), for: .touchUpInside)
self.rightView = button
self.rightViewMode = .always
}
#IBAction func togglePasswordView(_ sender: Any) {
self.isSecureTextEntry.toggle()
setPasswordToggleImage(sender as! UIButton)
}
}
Also I have a bug that places my icon in far right, instead of the place I meant and I don't know why but it's 2nd icon
You can use this directly to add image and padding to your UITextField. Can be done from code as well as storyboard
// MARK: - UITextField
extension UITextField {
#IBInspectable var leftPadding: CGFloat {
get {
return leftView?.frame.size.width ?? 0.0
}
set {
let frame = CGRect(x: 0, y: 0, width: newValue, height: bounds.size.height)
leftView = UIView(frame: frame)
leftViewMode = .always
}
}
#IBInspectable var leftImage: UIImage? {
get {
let imgView = leftView?.subviews.first(where: {$0 is UIImageView}) as? UIImageView
return imgView?.image
}
set {
let frame = CGRect(x: 0, y: 0, width: leftPadding, height: bounds.size.height)
leftView = UIView(frame: frame)
let imgView = UIImageView(frame: frame.insetBy(dx: 4, dy: 4))
imgView.contentMode = .scaleAspectFit
leftView?.addSubview(imgView)
imgView.image = newValue
leftViewMode = .always
}
}
#IBInspectable var rightPadding: CGFloat {
get {
return rightView?.frame.size.width ?? 0.0
}
set {
let frame = CGRect(x: 0, y: 0, width: newValue, height: bounds.size.height)
rightView = UIView(frame: frame)
rightViewMode = .always
}
}
#IBInspectable var rightImage: UIImage? {
get {
let imgView = rightView?.subviews.first(where: {$0 is UIImageView}) as? UIImageView
return imgView?.image
}
set {
let frame = CGRect(x: 0, y: 0, width: rightPadding, height: bounds.size.height)
rightView = UIView(frame: frame)
let imgView = UIImageView(frame: frame.insetBy(dx: 4, dy: 4))
imgView.contentMode = .scaleAspectFit
rightView?.addSubview(imgView)
imgView.image = newValue
rightViewMode = .always
}
}
}
When user open my app for the first time, my custom view doesn't appear. But the next time it works fine.
I am calling this view when user taps on button.
let menu = MenuView(image: image, title: "text", buttons: buttons)
menu.show(animated: true)
Custom View code
class MenuView: UIView, Menu {
var background = UIView()
var blackOverlay = UIView()
convenience init(image: UIImage, title: String, buttons: [UIButton]) {
self.init(frame: UIScreen.main.bounds)
setupView(image: image, title: title, buttons: buttons)
}
#objc func cancelTapped() {
hide(animated: true)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView(image: UIImage, title: String, buttons: [UIButton]) {
blackOverlay.backgroundColor = UIColor.black.withAlphaComponent(0.5)
blackOverlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
addSubview(blackOverlay)
let backgroundWidth = self.frame.width - CGFloat(80.0)
let imageView = UIImageView(frame: CGRect(x: (backgroundWidth/2)-17, y: 40, width: 34, height: 34))
imageView.image = image
imageView.contentMode = .center
background.addSubview(imageView)
let titleLabel = UILabel()
let stringHeight = title.stringHeight + 14
titleLabel.frame = CGRect(x: background.center.x+8, y: 100, width: backgroundWidth-16, height: stringHeight)
titleLabel.font = UIFont.systemFont(ofSize: 15, weight: .regular)
titleLabel.text = title
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
background.addSubview(titleLabel)
var newHeight: CGFloat = 0
for i in 0...buttons.count-1 {
buttons[i].frame.origin = CGPoint(x: 0, y: titleLabel.frame.height + 125 + CGFloat(i*50))
buttons[i].frame.size = CGSize(width: backgroundWidth, height: 50)
buttons[i].setTitleColor(.gingerColor, for: .normal)
buttons[i].setTitleColor(UIColor.gingerColor.withAlphaComponent(0.5), for: .highlighted)
buttons[i].titleLabel?.textAlignment = .center
newHeight+=buttons[i].frame.height
let separator = UIView()
separator.frame.origin = CGPoint(x: 0, y: titleLabel.frame.height + 125 + CGFloat(i*50))
separator.frame.size = CGSize(width: frame.width, height: 1)
separator.backgroundColor = UIColor(hexString: "dedede")
background.addSubview(separator)
if i == buttons.count-1 {
buttons[i].setTitleColor(UIColor(hexString: "9E9E9E"), for: .normal)
}
buttons[i].addTarget(self, action: #selector(self.cancelTapped), for: .touchUpInside)
background.addSubview(buttons[i])
}
background.frame.origin = CGPoint(x: center.x, y: frame.height)
background.frame.size = CGSize(width: frame.width-80, height: 90 + titleLabel.frame.height+imageView.frame.height + CGFloat(newHeight))
background.backgroundColor = .white
background.layer.cornerRadius = 16
background.layer.masksToBounds = true
addSubview(background)
}
}
and my custom view protocol & extension code:
protocol Menu {
func show(animated: Bool)
func hide(animated: Bool)
var blackOverlay: UIView { get }
var background: UIView { get }
}
extension Menu where Self: UIView {
func show(animated: Bool) {
self.blackOverlay.alpha = 0
self.background.alpha = 0
self.blackOverlay.center = self.center
self.background.center = CGPoint(x: self.center.x, y: self.frame.height+self.background.frame.height/2)
UIApplication.shared.delegate?.window??.rootViewController?.view.addSubview(self)
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.blackOverlay.alpha = 1
self.background.alpha = 1
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.background.center = self.center
}, completion: { (completed) in
print("completed is \(completed)")
})
} else {
self.blackOverlay.alpha = 1
self.background.alpha = 1
self.background.center = self.center
}
}
func hide(animated: Bool) {
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.blackOverlay.alpha = 0
self.background.alpha = 0
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.background.center = CGPoint(x: self.center.x, y: self.frame.height + self.background.frame.height/2)
}, completion: { (completed) in
self.removeFromSuperview()
})
} else {
self.removeFromSuperview()
}
}
}
As you can see I have a parameter completed in completion animation block, it returns false for the first time. All subsequent times it returns false
I found my mistake.
I tried to present my custom view controller above rootController. But, for the very first time user's rootcontroller is the first screen of onboarding. So, my custom view tried to be shown on another controller.
Just trying to follow some youtube tutorials and learn about custom views. I am sure I am missing something very basic here. After reading here and the net for hours I am giving up.
I have a custom view that slides out. I am planning on filling it with different custom views as needed. The slideOutView displays fine. However, I am not able to see the other custom view called FlashCardView inside it. I am calling FlashCardView from SlideOutView.
Thanks for the help!
code for FlashCardView class:
import UIKit
protocol FlashCardViewDelegate: class {
}
class FlashCardView: UIView {
weak var delegate: FlashCardViewDelegate?
let questionLabel = UILabel()
let scrollView = UIScrollView()
let dummyView = UIView()
//MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.black
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
code for SlideOutView class:
import UIKit
class SlideOutView: UIView {
let slideOutView = UIView()
let menuBarLine = UIView()
let menuBarView = UIView()
let menuButton = UIButton()
let backButton = UIButton()
let flashCardView = FlashCardView()
let ratio = DeviceSpecificValues()
override init(frame: CGRect) {
super.init(frame: frame)
self.addView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addView() {
if let window = UIApplication.shared.keyWindow {
slideOutView.frame = CGRect(x: 0, y: window.bounds.height, width: window.bounds.width, height: window.bounds.height / 2)
print("slideoutview frame is: \(slideOutView.frame)")
slideOutView.backgroundColor = UIColor.white
window.addSubview(slideOutView)
menuBarView.frame = CGRect(x: 0, y: 0, width: slideOutView.bounds.width, height: window.bounds.height / ratio.menuBarRatio)
menuBarView.backgroundColor = GlobalValues.menuBarViewColor
slideOutView.addSubview(menuBarView)
menuBarLine.frame = CGRect(x: 0, y: 0, width: menuBarView.bounds.width, height: GlobalValues.menuBarLineWidth)
menuBarLine.backgroundColor = GlobalValues.menuTextColor
menuBarView.addSubview(menuBarLine)
backButton.frame = CGRect(x: 0, y: 0, width: menuBarView.bounds.width * 0.1, height: menuBarView.bounds.height * 0.9)
backButton.center.y = menuBarView.bounds.height / 2 + GlobalValues.menuBarLineWidth
backButton.setTitle("<Back", for: .normal)
backButton.titleLabel?.adjustsFontSizeToFitWidth = true
backButton.setTitleColor(GlobalValues.menuTextColor, for: UIControlState())
backButton.showsTouchWhenHighlighted = true
menuBarView.addSubview(backButton)
menuButton.frame = CGRect(x: menuBarView.bounds.width * 0.9, y: 0, width: menuBarView.bounds.width * 0.1, height: menuBarView.bounds.height * 0.9)
menuButton.center.y = menuBarView.bounds.height / 2 + GlobalValues.menuBarLineWidth
menuButton.setTitle("Menu", for: .normal)
menuButton.titleLabel?.adjustsFontSizeToFitWidth = true
menuButton.setTitleColor(GlobalValues.menuTextColor, for: UIControlState())
menuButton.showsTouchWhenHighlighted = true
menuBarView.addSubview(menuButton)
addFlashCardView()
}
}
func slideIn() {
UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveLinear, animations: {
if let window = UIApplication.shared.keyWindow {
self.slideOutView.frame = CGRect(x: 0, y: window.bounds.height, width: window.bounds.width, height: window.bounds.height / 2)
}
}, completion: nil)
}
func slideOut() {
UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveLinear, animations: {
if let window = UIApplication.shared.keyWindow {
self.slideOutView.frame = CGRect(x: 0, y: window.bounds.height / 2, width: window.bounds.width, height: window.bounds.height / 2)
}
}, completion: nil)
}
func addFlashCardView() {
flashCardView.frame = slideOutView.frame
slideOutView.addSubview(flashCardView)
}
}
I have a circle that I'm animating. It works except that the drawing is from the top left.. Can I animate it from the center? If so, any help would be appreciated..
My code for drawing the circle is:
class CircleView: UIView {
override func draw(_ rect: CGRect)
{
let prefs: UserDefaults = UserDefaults.standard
lineWidthFloat = prefs.value(forKey: "lineWidth") as! Float
let circleSize = Double(lineWidthFloat * 100)
let context = UIGraphicsGetCurrentContext()
context!.setLineWidth(10.0)
context!.setFillColor(UIColor.black.cgColor)
let rect = CGRect(x: 20, y: 20, width: circleSize, height: circleSize)
context!.addEllipse(inRect: rect)
context!.fillPath()
}
}
Thanks!
Details
Xcode 10.2.1 (10E1001), Swift 5
Full Sample
CircleView
class CircleView: UIView {
weak var circleView: UIView?
lazy var isAnimating = false
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func setup() {
let rectSide = (frame.size.width > frame.size.height) ? frame.size.height : frame.size.width
let circleRect = CGRect(x: (frame.size.width-rectSide)/2, y: (frame.size.height-rectSide)/2, width: rectSide, height: rectSide)
let circleView = UIView(frame: circleRect)
circleView.backgroundColor = UIColor.yellow
circleView.layer.cornerRadius = rectSide/2
circleView.layer.borderWidth = 2.0
circleView.layer.borderColor = UIColor.red.cgColor
addSubview(circleView)
self.circleView = circleView
}
func resizeCircle (summand: CGFloat) {
guard let circleView = circleView else { return }
frame.origin.x -= summand/2
frame.origin.y -= summand/2
frame.size.height += summand
frame.size.width += summand
circleView.frame.size.height += summand
circleView.frame.size.width += summand
}
private func animateChangingCornerRadius (toValue: Any?, duration: TimeInterval) {
guard let circleView = circleView else { return }
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.fromValue = circleView.layer.cornerRadius
animation.toValue = toValue
animation.duration = duration
circleView.layer.cornerRadius = circleView.frame.size.width/2
circleView.layer.add(animation, forKey:"cornerRadius")
}
private func circlePulseAinmation(_ summand: CGFloat, duration: TimeInterval, completionBlock:#escaping ()->()) {
guard let circleView = circleView else { return }
UIView.animate(withDuration: duration, delay: 0, options: .curveEaseInOut, animations: { [weak self] in
self?.resizeCircle(summand: summand)
}) { _ in completionBlock() }
animateChangingCornerRadius(toValue: circleView.frame.size.width/2, duration: duration)
}
func resizeCircleWithPulseAinmation(_ summand: CGFloat, duration: TimeInterval) {
if (!isAnimating) {
isAnimating = true
circlePulseAinmation(summand, duration:duration) { [weak self] in
guard let self = self else { return }
self.circlePulseAinmation((-1)*summand, duration:duration) {self.isAnimating = false}
}
}
}
}
ViewController
import UIKit
class ViewController: UIViewController {
weak var circleView: CircleView?
weak var button: UIButton?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let circleView = CircleView(frame: CGRect(x: 40, y: 50, width: 40, height: 60))
circleView.backgroundColor = UIColor.clear
view.addSubview(circleView)
self.circleView = circleView
let button = UIButton(frame: CGRect(x: 20, y: 150, width: 80, height: 40))
button.setTitle("Animate", for: UIControl.State())
button.setTitleColor(UIColor.blue, for: UIControl.State())
button.setTitleColor(UIColor.blue.withAlphaComponent(0.3), for: .highlighted)
button.addTarget(self, action: #selector(ViewController.animateCircle), for: .touchUpInside)
view.addSubview(button)
self.button = button
}
#objc func animateCircle() {
circleView?.resizeCircleWithPulseAinmation(30, duration: 1.5)
}
}
Result
You should be able to do it a lot simpler using the transform property.
func pulse() {
UIView.animate(withDuration: 0.5, animations:{
self.circleView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}, completion: { _ in
UIView.animate(withDuration: 0.5, animations: {
self.circleView.transform = .identity
})
})
}
I want to leave a bit of space at the beginning of a UITextField, just like here:
Add lefthand margin to UITextField
But I don't know how to do that with Swift.
This is what I am using right now:
Swift 4.2, 5
class TextField: UITextField {
let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
override open func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override open func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
}
Swift 4
class TextField: UITextField {
let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
override open func textRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
override open func editingRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
}
Swift 3:
class TextField: UITextField {
let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
override func textRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
}
I never set a other padding but you can tweak. This class doesn't take care of the rightView and leftView on the textfield. If you want that to be handle correctly you can use something like (example in objc and I only needed the rightView:
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect paddedRect = UIEdgeInsetsInsetRect(bounds, self.insets);
if (self.rightViewMode == UITextFieldViewModeAlways || self.rightViewMode == UITextFieldViewModeUnlessEditing) {
return [self adjustRectWithWidthRightView:paddedRect];
}
return paddedRect;
}
- (CGRect)placeholderRectForBounds:(CGRect)bounds {
CGRect paddedRect = UIEdgeInsetsInsetRect(bounds, self.insets);
if (self.rightViewMode == UITextFieldViewModeAlways || self.rightViewMode == UITextFieldViewModeUnlessEditing) {
return [self adjustRectWithWidthRightView:paddedRect];
}
return paddedRect;
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
CGRect paddedRect = UIEdgeInsetsInsetRect(bounds, self.insets);
if (self.rightViewMode == UITextFieldViewModeAlways || self.rightViewMode == UITextFieldViewModeWhileEditing) {
return [self adjustRectWithWidthRightView:paddedRect];
}
return paddedRect;
}
- (CGRect)adjustRectWithWidthRightView:(CGRect)bounds {
CGRect paddedRect = bounds;
paddedRect.size.width -= CGRectGetWidth(self.rightView.frame);
return paddedRect;
}
This is a great case for an extension. By using an extension, there is no need to subclass UITextField and the new functionality will be made available to any UITextField in your app:
extension UITextField {
func setLeftPaddingPoints(_ amount:CGFloat){
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.frame.size.height))
self.leftView = paddingView
self.leftViewMode = .always
}
func setRightPaddingPoints(_ amount:CGFloat) {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.frame.size.height))
self.rightView = paddingView
self.rightViewMode = .always
}
}
When I need to set the padding of a text field anywhere in my application, I simply do the following:
textField.setLeftPaddingPoints(10)
textField.setRightPaddingPoints(10)
Using Swift extensions, the functionality is added to the UITextField directly without subclassing.
X, Y , Z are your desired values
textField.layer.sublayerTransform = CATransform3DMakeTranslation(x, y, z)
Such margin can be achieved by setting leftView / rightView to UITextField.
Updated For Swift 4
// Create a padding view for padding on left
textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: textField.frame.height))
textField.leftViewMode = .always
// Create a padding view for padding on right
textField.rightView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: textField.frame.height))
textField.rightViewMode = .always
I just added/placed an UIView to left and right side of the textfield. So now the typing will start after the view.
Thanks
Hope this helped...
Swift 4, Xcode 9
I like Pheepster's answer, but how about we do it all from the extension, without requiring VC code or any subclassing:
import UIKit
#IBDesignable
extension UITextField {
#IBInspectable var paddingLeftCustom: CGFloat {
get {
return leftView!.frame.size.width
}
set {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
leftView = paddingView
leftViewMode = .always
}
}
#IBInspectable var paddingRightCustom: CGFloat {
get {
return rightView!.frame.size.width
}
set {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
rightView = paddingView
rightViewMode = .always
}
}
}
Use my extension Swift 5 tested:
extension UITextField {
enum PaddingSpace {
case left(CGFloat)
case right(CGFloat)
case equalSpacing(CGFloat)
}
func addPadding(padding: PaddingSpace) {
self.leftViewMode = .always
self.layer.masksToBounds = true
switch padding {
case .left(let spacing):
let leftPaddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
self.leftView = leftPaddingView
self.leftViewMode = .always
case .right(let spacing):
let rightPaddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
self.rightView = rightPaddingView
self.rightViewMode = .always
case .equalSpacing(let spacing):
let equalPaddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
// left
self.leftView = equalPaddingView
self.leftViewMode = .always
// right
self.rightView = equalPaddingView
self.rightViewMode = .always
}
}
}
How to use
// equal padding
yourTextField.addPadding(padding: .equalSpacing(10))
// padding right
yourTextField.addPadding(padding: .right(10))
// padding left
yourTextField.addPadding(padding: .left(10))
in Swift 4.2 and Xcode 10
Initially my text field is like this.
After adding padding in left side my text field is...
//Code for left padding
textFieldName.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: textFieldName.frame.height))
textFieldName.leftViewMode = .always
Like this we can create right side also.(textFieldName.rightViewMode = .always)
If you want SharedInstance type code(Write once use every ware) see the below code.
//This is my shared class
import UIKit
class SharedClass: NSObject {
static let sharedInstance = SharedClass()
//This is my padding function.
func textFieldLeftPadding(textFieldName: UITextField) {
// Create a padding view
textFieldName.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 3, height: textFieldName.frame.height))
textFieldName.leftViewMode = .always//For left side padding
textFieldName.rightViewMode = .always//For right side padding
}
private override init() {
}
}
Now call this function like this.
//This single line is enough
SharedClass.sharedInstance.textFieldLeftPadding(textFieldName:yourTF)
Simple swift 3 solution - add code to viewDidLoad:
let indentView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 20))
textField.leftView = indentView
textField.leftViewMode = .always
No need for ridiculously long code
Here is Haagenti's answer updated to Swift 4.2:
class PaddedTextField: UITextField {
func getPadding(plusExtraFor clearButtonMode: ViewMode) -> UIEdgeInsets {
var padding = UIEdgeInsets(top: 11, left: 16, bottom: 11, right: 16)
// Add additional padding on the right side when showing the clear button
if self.clearButtonMode == .always || self.clearButtonMode == clearButtonMode {
padding.right = 28
}
return padding
}
override open func textRect(forBounds bounds: CGRect) -> CGRect {
let padding = getPadding(plusExtraFor: .unlessEditing)
return bounds.inset(by: padding)
}
override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {
let padding = getPadding(plusExtraFor: .unlessEditing)
return bounds.inset(by: padding)
}
override open func editingRect(forBounds bounds: CGRect) -> CGRect {
let padding = getPadding(plusExtraFor: .whileEditing)
return bounds.inset(by: padding)
}
}
Reference: Upgrading To Swift 4.2.
Edit: Account for clear button.
To create padding view for UITextField in Swift 5
func txtPaddingVw(txt:UITextField) {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: 5))
txt.leftViewMode = .always
txt.leftView = paddingView
}
This one line of code saved me:
For Xamarin.iOS:
textField.Layer.SublayerTransform = CATransform3D.MakeTranslation(5, 0, 0);
For Swift:
textField.layer.sublayerTransform = CATransform3DMakeTranslation(5, 0, 0);
Subclassing UITextField is the way to go. Open a playground and add the following code:
class MyTextField : UITextField {
var leftTextMargin : CGFloat = 0.0
override func textRectForBounds(bounds: CGRect) -> CGRect {
var newBounds = bounds
newBounds.origin.x += leftTextMargin
return newBounds
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
var newBounds = bounds
newBounds.origin.x += leftTextMargin
return newBounds
}
}
let tf = MyTextField(frame: CGRect(x: 0, y: 0, width: 100, height: 44))
tf.text = "HELLO"
tf.leftTextMargin = 25
tf.setNeedsLayout()
tf.layoutIfNeeded()
Easy way: to do this by extending UITextField
extension UITextField {
func setPadding(left: CGFloat? = nil, right: CGFloat? = nil){
if let left = left {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: left, height: self.frame.size.height))
self.leftView = paddingView
self.leftViewMode = .always
}
if let right = right {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: right, height: self.frame.size.height))
self.rightView = paddingView
self.rightViewMode = .always
}
}
}
Then you can set padding to any edge this way:
textField.setPadding(left: 5, right: 5)
In most cases you can regard this as a technicality but all the examples don't get the difference between frame and bounds right. When referencing the view's height in a subview, use bounds – otherwise you may run into trouble once some transform is applied to the parent.
See the updated code below (based on Pheepster's answer).
extension UITextField {
func setLeftPadding(_ amount: CGFloat = 10) {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.bounds.size.height))
self.leftView = paddingView
self.leftViewMode = .always
}
func setRightPadding(_ amount: CGFloat = 10) {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.bounds.size.height))
self.rightView = paddingView
self.rightViewMode = .always
}
}
Create UIView with required padding space and add it to textfield.leftView member and set textfield.leftViewMode member to UITextFieldViewMode.Always
// For example if you have textfield named title
#IBOutlet weak var title: UITextField!
// Create UIView
let paddingView : UIView = UIView(frame: CGRectMake(0, 0, 5, 20))
//Change your required space instaed of 5.
title.leftView = paddingView
title.leftViewMode = UITextFieldViewMode.Always
I prefer to use IBDesignable class and IBInspectable properties to allow me to set the padding via Xcode storyboards and keep it reusable. I've also updated the code to work in Swift 4.
import Foundation
import UIKit
#IBDesignable
class PaddableTextField: UITextField {
var padding = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
#IBInspectable var left: CGFloat = 0 {
didSet {
adjustPadding()
}
}
#IBInspectable var right: CGFloat = 0 {
didSet {
adjustPadding()
}
}
#IBInspectable var top: CGFloat = 0 {
didSet {
adjustPadding()
}
}
#IBInspectable var bottom: CGFloat = 0 {
didSet {
adjustPadding()
}
}
func adjustPadding() {
padding = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right))
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right))
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right))
}
}
Put this code in your viewDidLoad():
textField.delegate = self
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: self.textField.frame.height))
textField.leftView = paddingView
textField.leftViewMode = UITextFieldViewMode.always
It works for me :)
ScareCrow's answer in Swift 3
let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5);
override func textRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(bounds, padding)
}
In Swift 3.
You may use custom UITextField with indent that is set in its constructor. Don't need for extra declaration in a controller.
class CustomTextField : UITextField {
private let indentView = UIView(frame: CGRect(x: 0, y:0, width: 10, height: 10))
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.leftView = indentView
self.leftViewMode = .always
}
}
* Extending UITextField in Swift 5 *
import UIKit
#IBDesignable
extension UITextField {
#IBInspectable var paddingLeftCustom: CGFloat {
get {
return leftView!.frame.size.width
}
set {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
leftView = paddingView
leftViewMode = .always
}
}
#IBInspectable var paddingRightCustom: CGFloat {
get {
return rightView!.frame.size.width
}
set {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
rightView = paddingView
rightViewMode = .always
}
}
}
//MARK:- Use this class for different type of Roles
import UIKit
class HelperExtensionViewController: UIViewController {
}
//MARK:- Extension
extension UIImageView
{
func setImageCornerRadius()
{
self.layer.cornerRadius = self.frame.size.height/2
self.clipsToBounds = true
}
func setImageCornerRadiusInPoints(getValue:CGFloat)
{
self.layer.cornerRadius = getValue
self.clipsToBounds = true
}
}
extension UIButton
{
func setButtonCornerRadiusOnly()
{
self.layer.cornerRadius = self.frame.size.height/2
self.clipsToBounds = true
}
func setBtnCornerRadiusInPoints(getValue:CGFloat)
{
self.layer.cornerRadius = getValue
self.clipsToBounds = true
}
}
extension UITextField
{
func setTextFieldCornerRadiusWithBorder()
{
self.layer.cornerRadius = self.frame.size.height/2
self.layer.borderColor = UIColor.darkGray.cgColor
self.backgroundColor = UIColor.clear
self.layer.borderWidth = 0.5
self.clipsToBounds = true
}
func setLeftPaddingPoints(_ amount:CGFloat){
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.frame.size.height))
self.leftView = paddingView
self.leftViewMode = .always
}
func setRightPaddingPoints(_ amount:CGFloat) {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: self.frame.size.height))
self.rightView = paddingView
self.rightViewMode = .always
}
}
extension UIView
{
func setCornerRadius()
{
self.layer.cornerRadius = self.frame.size.height/2
self.clipsToBounds = true
}
// OUTPUT 1
func setViewCornerRadiusWithBorder()
{
self.layer.cornerRadius = self.frame.size.height/2
self.layer.borderColor = UIColor.init(red: 95.0/255.0, green: 229.0/255.0, blue: 206.0/255.0, alpha: 1.0).cgColor
self.backgroundColor = UIColor.clear
self.layer.borderWidth = 1.0
self.clipsToBounds = true
}
func layoutSubviews(myView:UIView)
{
let shadowPath = UIBezierPath(rect: myView.bounds)
myView.layer.masksToBounds = false
myView.layer.shadowColor = UIColor.lightGray.cgColor
myView.layer.shadowOffset = CGSize(width: -1.0, height: 2.0)
myView.layer.shadowOpacity = 0.5
myView.layer.shadowPath = shadowPath.cgPath
}
func layoutSubviews2(myView:UIView)
{
let shadowPath = UIBezierPath(rect: myView.bounds)
myView.clipsToBounds = true
myView.layer.masksToBounds = false
myView.layer.shadowColor = UIColor.black.cgColor
myView.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
myView.layer.shadowOpacity = 0.2
myView.layer.shadowPath = shadowPath.cgPath
}
func setViewCornerRadiusInPoints(getValue:CGFloat)
{
self.layer.cornerRadius = getValue
self.clipsToBounds = true
}
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -1, height: 1)
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
// OUTPUT 2
func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, radius: CGFloat = 1, scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = color.cgColor
layer.shadowOpacity = opacity
layer.shadowOffset = offSet
layer.shadowRadius = radius
layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
func setGradientBackground(myview:UIView) {
let colorTop = UIColor(red: 100.0/255.0, green: 227.0/255.0, blue: 237.0/255.0, alpha: 1.0).cgColor
let colorBottom = UIColor(red: 141.0/255.0, green: 109.0/255.0, blue: 164.0/255.0, alpha: 1.0).cgColor
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [colorTop, colorBottom]
gradientLayer.locations = [1.0, 1.0]
gradientLayer.frame = myview.bounds
myview.layer.insertSublayer(gradientLayer, at:0)
}
}
To Extend original answer for leftView and Swift5+
class TextField: UITextField {
let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
override open func textRect(forBounds bounds: CGRect) -> CGRect {
let paddedRect = bounds.inset(by: self.padding)
if (self.leftViewMode == .always || self.leftViewMode == .unlessEditing) {
return self.adjustRectOriginForLeftView(bounds: paddedRect)
}
return paddedRect
}
override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {
let paddedRect = bounds.inset(by: self.padding)
if (self.leftViewMode == .always || self.leftViewMode == .unlessEditing) {
return self.adjustRectOriginForLeftView(bounds: paddedRect)
}
return paddedRect;
}
override open func editingRect(forBounds bounds: CGRect) -> CGRect {
let paddedRect = bounds.inset(by: self.padding);
if (self.leftViewMode == .always || self.leftViewMode == .unlessEditing) {
return self.adjustRectOriginForLeftView(bounds: paddedRect)
}
return paddedRect;
}
func adjustRectOriginForLeftView(bounds : CGRect) -> CGRect{
var paddedRect = bounds;
paddedRect.origin.x += self.leftView!.frame.width
return paddedRect
}
}
Create space at the beginning of a UITextField.
in Swift 5+ and Xcode 12.
textFieldName.setLeftPaddingPoints(CGFloat(10))
textFieldName.setRightPaddingPoints(CGFloat(10)