I'm trying to do a clone of Instagram and I little bit stuck with a UITextField customization. Maybe is a noob question but here it goes.
What I'm trying to achieve if to have different inset for my UITextField, one smaller on the left side and one bigger on the right side, so I have space enough for the clear button from UITextField.
I'm trying to achieve this:
Instagram app
but the closest thing I get is this:
Clone app
As you can see the clear button is much more squeeze on my version, but I can't handle to inset only the right side a little bit more, without touching the left one.
I used the following code on my UITextField subclass (Swift 3):
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 16, dy: 0)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 16, dy: 0)
}
But it only let me do it for booth side changing dx. Any suggestion? Thanks in advance
This works for me
let rightView = UIView(frame: CGRectMake(0, 0, 16, 16))
textField.rightViewMode = UITextFieldViewMode.Always
textField.rightView = rightView
let leftView = UIView(frame: CGRectMake(0, 0, 20, 20))
textField.leftViewMode = UITextFieldViewMode.Always
textField.leftView = leftView
May it helps you.
Managed to solve it by myself:
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: 16, dy: 0)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + 16, y: bounds.origin.y, width: bounds.width - 56, height: bounds.height)
}
override func clearButtonRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.width - 36, y: bounds.origin.y, width: 16, height: bounds.height)
}
This way it work just like Instagram. It inset a little bit more the right side when is in editing mode, and when is in normal mode it has a inset of 16 both side.
Related
I want to pose my problem with the hope that someone can help me:
I created a subclass of UINavigationBar, adding an image as a background.
here is the code:
class NavigationBarN: UINavigationBar {
let customHeight: CGFloat = UIScreen.main.bounds.height / 8
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
translatesAutoresizingMaskIntoConstraints = true
self.barStyle = UIBarStyle.black
self.tintColor = .white
}
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = true
self.barStyle = UIBarStyle.black
self.tintColor = .white
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: customHeight)
}
var i = 0
override open func layoutSubviews() {
super.layoutSubviews()
frame = CGRect(x: frame.origin.x, y: 0, width: frame.size.width, height: customHeight) // problem here
for subview in self.subviews {
var stringFromClass = NSStringFromClass(subview.classForCoder)
if stringFromClass.contains("BarBackground") {
subview.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
let imageViewBackground = UIImageView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: customHeight))
imageViewBackground.image = UIImage(named: “Image”1)
imageViewBackground.contentMode = .scaleToFill
subview.addSubview(imageViewBackground)
}
stringFromClass = NSStringFromClass(subview.classForCoder)
if stringFromClass.contains("BarContent") {
let centerY = subview.frame.heigh
subview.frame = CGRect(x: subview.frame.origin.x, y: centerY, width: subview.frame.width, height: subview.frame.height)
}
}
}
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
}
Up to iOS 11, it works without problems since iOS 12 the problems begin, the subclass enters an infinite loop. However the infinite loop occurs only when I try to initialize a new VC through the "navigationController? .PushViewController" function, otherwise it works.
The line of code causing the loop apparently is:
frame = CGRect(x: frame.origin.x, y: 0, width: frame.size.width, height: customHeight)
But removing it the navigation bar is not created correctly.
Anyone have any solution?
Thank You
You shouldn't change your frame in layoutSubviews – you should only be laying out your subviews. Changing the size of the frame in layoutSubviews couples the layout and sizing of the view, which you shouldn't do.
It's likely that under the hood, UINavigationBar is calling setNeedsLayout when its frame gets set.
For my UITextField, I want the border to be removed. So, I tried changing its border color to the background color (with border width of 1) but doing so, faint border lines are seen at the corners. Next, I set the border style to none in the attribute inspector. When I run the app, the border is gone. However, when the text is entered in it, the text gets cropped in the left side as shown in the image. I tried adding padding view to the textfield but it did not fix the issue. How can I solve this?
EDIT:
The textfield is followed by a label. Since I want the label to follow the textfield content, I have not set the width of the textfield. This is shown in the image. When I add padding view with leftViewMode as always, the design in not rendered, and i get the console message:
- changing property masksToBounds in transform-only layer, will have no effect
Following one of the answers from #Surjeet's link, I tried extending the textField as:
class CustomTextField: UITextField {
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + 10, y: bounds.origin.y, width: bounds.size.width, height: bounds.size.height)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return self.textRect(forBounds: bounds)
}
}
But still the problem is not solved.
You can add left padding for your textfield and your issue should be resolved.
Swift 4
let paddingView: UIView = UIView.init(frame: CGRect(x: 0, y: 0, width: 5, height: 20))
textField.leftView = paddingView
textField.leftViewMode = .always
text_field.setLeftPaddingPoints(10)
text_field.setRightPaddingPoints(10)
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
}
}
I have a textfield called
nameTextField
I rounded the corners with the
nameTexfield.layer.cornerRadius = 5
Now, the text within the textfield is touching the left side. I want to create padding between the text and border. Every post I find uses cgRect, but Swift no longer supports that. Please provide the correct code if you can figure it out and please explain the answer if you can. I appreciate the help! I also need to know where to put the code if there is any.
Customized way to add padding in "left", "right" or "both" side od UITextField.
Step 1:- Add this UITextfield extension
extension UITextField {
enum PaddingSide {
case left(CGFloat)
case right(CGFloat)
case both(CGFloat)
}
func addPadding(_ padding: PaddingSide) {
self.leftViewMode = .always
self.layer.masksToBounds = true
switch padding {
case .left(let spacing):
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
self.leftView = paddingView
self.rightViewMode = .always
case .right(let spacing):
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
self.rightView = paddingView
self.rightViewMode = .always
case .both(let spacing):
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
// left
self.leftView = paddingView
self.leftViewMode = .always
// right
self.rightView = paddingView
self.rightViewMode = .always
}
}
}
Step 2: How to use
// 1. To add left padding
yourTextFieldName.addPadding(.left(20))
// 2. To add right padding
yourTextFieldName.addPadding(.right(20))
// 3. To add left & right padding both
yourTextFieldName.addPadding(.both(20))
As #the4kman says, Swift does support CGRect but the syntax may have changed.
You can try this for instance:
#IBOutlet weak var nameTextField: UITextField! {
didSet {
nameTextField.layer.cornerRadius = 5
nameTextField.layer.borderColor = UIColor.black.cgColor
nameTextField.layer.borderWidth = 1
let leftView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 2.0))
nameTextField.leftView = leftView
nameTextField.leftViewMode = .always
}
}
If I do that, I get this fine result
Hope that helps.
Update
You ask for a function instead of setting it in didSet and sure, thats possible, something like:
func addPaddingAndBorder(to textfield: UITextField) {
textfield.layer.cornerRadius = 5
textfield.layer.borderColor = UIColor.black.cgColor
textfield.layer.borderWidth = 1
let leftView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 2.0))
textfield.leftView = leftView
textfield.leftViewMode = .always
}
and then you' call that in viewDidLoad for instance like so:
override func viewDidLoad() {
super.viewDidLoad()
addPaddingAndBorder(to: nameTextField)
}
Better to subclass the textview than to add a left view incase you are planning to add an image as a left view.
class UITextFieldPadding: UITextField {
let padding = UIEdgeInsets(top: 0, left: 40, bottom: 0, right: 0)
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)
}
}
I'm pretty sure this is not duplicate because I looked at other answers and they all outdated and do not provide exact answer.
I have Navigation Controller and several view controllers. I want to make Navigation Bar a bit taller so it would fit text size that I need. How can I do that ?
I tried this:
UINavigationBar.appearance().frame = CGRect(x: 0.0, y: 0.0, width: 320.0, height: 210.0)
but nothing happens...
Also, I wasn't able to add any constraints to the Nav Bar using Xcode layout buttons.
Hope somebody can help me to fix this issue. Thanks in advance!
UPDATE 8/3/20: I posted this in 2016. A number of people have stated this no longer works so please use at your own risk. I am not working in iOS at the moment so I do not have an update handy. Best of luck!
Here is one way to do it:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let height: CGFloat = 50 //whatever height you want to add to the existing height
let bounds = self.navigationController!.navigationBar.bounds
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + height)
}
You can write this code in class that extends UINavigationController
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let height = CGFloat(72)
navigationBar.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: height)
}
You can create a class based on UINavigationBar like this
class TTNavigationBar: UINavigationBar {
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: 55)
}
}
And add it to your NavigationController.
This might be better if your navigation bars in your app all have same adjusted height.
extension UINavigationBar {
open override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: 51)
}
}
you can draw your navigation bar from xib (with your height) and then:
self.navigationController?.navigationBar.addSubview(yourNavBarView)
in alternative, if you want create UIView from code,
let viewNavBar = UIView(frame: CGRect(
origin: CGPoint(x: 0, y:0),
size: CGSize(width: self.view.frame.size.width, height: 100)))
and then adding to nav bar.
How to create some space at the beginning of a text field using swift?
I looked at the post
padding of text field
I don't understand what the subclass is doing and how do I use that class to padding.
Thank you very much
https://stackoverflow.com/a/27066764/846780
This answer is very clear,
If you subclass UITextField you can override textRectForBounds, editingRectForBounds and placeholderRectForBounds. These methods just allow you to add a rect (frame) to your textfield's label.
newBounds method create the rect (frame) that will be added to textfield's label.
Finally your padding is: let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5);
Now you have a custom UITextField that has a custom padding.
If you create your new subClass like this class MyTextField: UITextField for example, you only need to change the class of the UITextField that you've added into IB file.
Complementing the answer of klevison-matias this is the code I use when I want to add a padding to my TextField in Swift 3
//UITextField : override textRect, editingRect
class LeftPaddedTextField: UITextField {
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + 10, y: bounds.origin.y, width: bounds.width, height: bounds.height)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + 10, y: bounds.origin.y, width: bounds.width, height: bounds.height)
}
}
Then in my TextField I use in this way:
let emailTextField: LeftPaddedTextField = {
let textField = LeftPaddedTextField()
textField.placeholder = "Enter email"
textField.layer.borderColor = UIColor.lightGray.cgColor
textField.layer.borderWidth = 1
textField.keyboardType = .emailAddress
return textField
}()
let passwordTextField: LeftPaddedTextField = {
let textField = LeftPaddedTextField()
textField.placeholder = "Enter password"
textField.layer.borderColor = UIColor.lightGray.cgColor
textField.layer.borderWidth = 1
textField.isSecureTextEntry = true
return textField
}()
Create TextField outlet and use following code. Say "nameTextField" has to add padding.
let paddingView = UIView(frame: CGRectMake(x: 0, y: 0, width: 30, height: 30))
nameTextField.leftView = paddingView
nameTextField.leftViewMode = .always
nameTextField.placeholder = "Enter Name"
You can add Image in paddingView.
Based on #Jorge Casariego's answer I came up with this solution.
You can set PaddedTextField class and the desired padding through the Interface Builder!
class PaddedTextField: UITextField {
#IBInspectable var padding: CGFloat = 0
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + padding, y: bounds.origin.y, width: bounds.width - padding * 2, height: bounds.height)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + padding, y: bounds.origin.y, width: bounds.width - padding * 2, height: bounds.height)
}
}
full code sample:
make sure your TextField points to RoundedTextField via Story board
import UIKit
class RoundedTextField: UITextField {
override func awakeFromNib() {
// add rounded corner
self.layer.cornerRadius = 15.0
self.clipsToBounds = false
super.awakeFromNib()
}
// For the padding from the left
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + 15.0, y: bounds.origin.y, width: bounds.width, height: bounds.height)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + 15.0, y: bounds.origin.y, width: bounds.width, height: bounds.height)
}
}