Swift iOS - Bottom Border on TextField initial wrong width - ios

I implement a solution to put a bottom border to a textfield which works good but i have following Problem. If i start the app on bigger sizes (iPad mini, iPad Pro) or landscape (iPhone6, 6s) the line under the textfield is not correct stretched.
I have create a extension for the textfields:
extension UITextField {
/**
Customize the UITextField for App
- parameter isPasswordField: Boolean to check if this field is a password field
- author: Simon Zwicker <simon.zwicker#gmail.com>
*/
func customize(isPasswordField: Bool) {
let bottomLine = UIView()
bottomLine.frame = CGRect(x: 0.0, y: self.frame.size.height - 1, width: self.frame.size.width, height: 1.0)
bottomLine.backgroundColor = UIColor.grayColor()
self.addSubview(bottomLine)
self.tintColor = UIColor.grayColor()
if isPasswordField {
self.textColor = UIColor.blueColor()
}
}
I call the customize() function on the textfields in viewWillLayoutSubviews()
Did i make somewhere a mistake? After i put device in portrait and back to landscape it works.
The size of the textfield is initial correct but the self.frame.size.width in the customize function is initial too small. Do you know what happened maybe?

I was able to replicate your issue, and it seems like at the moment of calling viewWillLayoutSubviews the text field did not have the width that's shown on the screen in the end (or constraints weren't applied).
Calling customize in viewDidLayoutSubviews scaled the line properly. I am not sure if you are happy with this solution.

Related

How can I set an underline on a UITextField?

I am trying to set an underline on my UITextFields. I have tried a couple of methods but none of them seem to work. After looking through a couple of websites, the most suggested method is the following:
extension UITextField {
func setUnderLine() {
let border = CALayer()
let width = CGFloat(0.5)
border.borderColor = UIColor.lightGray.cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width-10, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
}
}
I can't think of any reason as to why the code above would not work, but all the answers I saw were posted a couple of years ago.
Could someone please let me know what I am doing wrong?
One problem I see with the code that you posted is that it won't update the layer if the text field gets resized. Each time you call the setUnderLine() function, it adds a new layer, then forgets about it.
I would suggest subclassing UITextField instead. That code could look like this:
class UnderlinedTextField: UITextField {
let underlineLayer = CALayer()
/// Size the underline layer and position it as a one point line under the text field.
func setupUnderlineLayer() {
var frame = self.bounds
frame.origin.y = frame.size.height - 1
frame.size.height = 1
underlineLayer.frame = frame
underlineLayer.backgroundColor = UIColor.blue.cgColor
}
// In `init?(coder:)` Add our underlineLayer as a sublayer of the view's main layer
required init?(coder: NSCoder) {
super.init(coder: coder)
self.layer.addSublayer(underlineLayer)
}
// in `init(frame:)` Add our underlineLayer as a sublayer of the view's main layer
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.addSublayer(underlineLayer)
}
// Any time we are asked to update our subviews,
// adjust the size and placement of the underline layer too
override func layoutSubviews() {
super.layoutSubviews()
setupUnderlineLayer()
}
}
That creates a text field that looks like this:
(And note that if you rotate the simulator to landscape mode, the UnderlineTextField repositions the underline layer for the new text field bounds.)
Note that it might be easier to just add a UIView to your storyboard, pinned to the bottom of your text field and one pixel tall, using your desired underline color. (You'd set up the underline view using AutoLayout constraints, and give it a background color.) If you did that you wouldn't need any code at all.
Edit:
I created a Github project demonstrating both approaches. (link)
I also added a view-based underline to my example app. That looks like this:

Troubles with my navBarItem

i have a navigation Bar Item. When the app loads, i have this code so the navbar has the height that i want...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let height: CGFloat = 38
let bounds = self.navigationController!.navigationBar.bounds
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + height)
}
The problem is that when i press the homeButton and then i return to the app, the navbar changes the height, and i want the height to be like the first time you run the app
It seems like when i first run the app, the height of the navbar takes the value that i want, but then, when i close and reopen the app, it takes the original value (the default height value of the navbar).
What should i do? Should i implement another method?? Thanks!!!
I would recommend use autolayout constraint properly in your storyboard file. And then you do not have to set the height programmatically. But if you would like to manipulate programmatically then move your code in ViewDidload instead of ViewDidAppear method.
let height: CGFloat = 38
let bounds = self.navigationController!.navigationBar.bounds
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + height)
In Storyboard select your Navaigation Item > Prompt Field add space to increase the height of Navigation bar
And have your code in either viewDidAppear(As you have today) or viewWillAppear
viewWillAppear or viewDidAppear methods are not designed to work like that. If your application goes to inactive and become active again while a UIViewController on the foreground it won't call any of those methods.
You can track that from applicationDidBecomeActive method in AppDelegate.
But if you want to know that in a specific view controller you can use the notification named UIApplicationDidBecomeActive
I think you should subclass the UINavigationController, but keep the navigationbar it self is the best, if you wat to add something on the bar, you can add a uitoolbar on the ViewController's view, and toolBar.frame = (0,64,screenWidth, 30), then delete the hair line of navigationbar , and it will look like the navigationbar been extended

swift: Centre alignment for the text of labels and buttons

Programmatically i have created a View and added a label and buttons to it. its fine in vertical it aligns to centre but as i rotate the screen it does not aligns to centre rather it seems as left aligned.
This is my code:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 150))
headerView.layer.borderWidth = 1
headerView.layer.borderColor = UIColor.whiteColor().CGColor
headerView.backgroundColor = ClientConfiguration.primaryUIColor()
let myLabel = UILabel()
myLabel.frame = CGRectMake(-1, -1, tableView.frame.width, 30)
myLabel.font = UIFont.boldSystemFontOfSize(14)
myLabel.backgroundColor = ClientConfiguration.primaryUIColor()
myLabel.textColor = UIColor.whiteColor()
myLabel.text = "Select Time Zone"
myLabel.textAlignment = .Center
let frame = CGRectMake(1, 1,headerView.frame.width , 70)
self.btnTimeZone.frame = frame
headerView.addSubview(myLabel)
headerView.addSubview(self.btnTimeZone)
self.buttonTitileString = self.selectedZone.value
self.btnTimeZone.setTitle(buttonTitileString, forState: .Normal)
return headerView
}
In horizontal mode the button text and label are aligned to left
and when i set self.btnTimeZone.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Center
its fine in horizontal mode but in vertical mode they all are right aligned as;
enter image description here
How can i solve this issue i need both of them in centre aligned in both horizontal and vertical mode.
You just need to reload the table when rotate the phone. Implement following method and inside of this method reload the tableView
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
tableView.reloadData()
}
In case of the application should support both portrait and landscape modes, the good approach would be to work with NSLayoutConstraint.
Of course you have both options to create them programmatically or from the Interface Builder, I'd like to note that you are able to create the header view as a cell, without the need of doing it programmatically; That's -obviously- will leads to the ease of setup the desired constrains for the header subviews.
Try this also
change
tableView.frame.width
to
tableView.frame.size.width
You could try to set autoresizing for parent view.
headerView.autoresizesSubviews = true
myLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.btnTimeZone.autoresizingMask = [.flexibleWidth, .flexibleHeight]
In Swift 5 (and 3, 4 too) you got to override viewDidLayoutSubviews() method of life cycle of view controller, it calls after viewWillTransition() method when rotating your device.
In viewDidLayoutSubviews() method you just need to reload the data of your tableView in order to redraw your section header for portrait to landscape or landscape to portrait, and it works perfectly.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.tableView.reloadData()
}
Thanks to GayashanK for give me this idea with his answer.

Swift: Creating a border or a thin line on top of the displaying Keyboard in iOS

as the topic already says, i try to create a small line on top of my UIKeyboard appearance. The reason is, if i pop up the white keyboard while having a white background, it looks just awful.
One idea would be to create a thin 1px line and place it on the bottom, hide it and show it whenever there is the keyboard displayed and align it with auto layout. The disadvantage of this idea is to do it in every view.
Thanks for your ideas.
Every UITextField and UITextView has an inputAccessoryView property (inherited from the UIResponder superclass). When one of these views becomes first responder and the system presents the on-screen keyboard, the system automatically displays the inputAccessoryView of the first responder along the top of the keyboard. For example, in this answer, the dark bar with the left and right arrows and the “Done” button is an inputAccessoryView.
So the easiest way for you to get a thin line on top of the keyboard is probably to create a one-point tall view and set it as the inputAccessoryView of your text field or text view. Set the background color of the accessory view to a dark color.
Easiest way to do this to add an inpustAccessoryView for textFiled when it becomes first responder and the system presents the on-screen keyboard.
Try this out:
Swift 3:
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
let separatorView = UIView(frame:CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 1))
separatorView.backgroundColor = UIColor.lightGray
textField.inputAccessoryView = separatorView
return true
}
So as Rob already wrote, the AccessoryView is the correct and smoothest solution, because Apple solves all animations, displaying and so on, in a correct way so no worry to take care about anything else.
I solved it as an extension. I wanted it to display a small devider on the bottom so i have a closed Numeric keyboard:
Swift 2.0:
extension UITextField {
override public func becomeFirstResponder() -> Bool {
self.addSeparatorInputAccessoryIfNumericKeyboardOnPhone()
return super.becomeFirstResponder()
}
func addSeparatorInputAccessoryIfNumericKeyboardOnPhone() {
guard self.keyboardType == .NumberPad else {
return
}
guard UIDevice.currentDevice().userInterfaceIdiom == .Phone else {
return
}
let view = UIView(frame: CGRectMake(0, 0, 0, 1 / UIScreen.mainScreen().scale))
view.backgroundColor = UIColor.lightGrayColor()
self.inputAccessoryView = view
}
}

How to display UIView over keyboard in iOS

I want to create a simple view over keyboard, when users tap "Attach" button in inputAccessoryView.
Something like this:
Is there an easy way to do it? Or i should create my custom keyboard?
You can add that new subview to your application window.
func attach(sender : UIButton)
{
// Calculate and replace the frame according to your keyboard frame
var customView = UIView(frame: CGRect(x: 0, y: self.view.frame.size.height-300, width: self.view.frame.size.width, height: 300))
customView.backgroundColor = UIColor.redColor()
customView.layer.zPosition = CGFloat(MAXFLOAT)
var windowCount = UIApplication.sharedApplication().windows.count
UIApplication.sharedApplication().windows[windowCount-1].addSubview(customView);
}
Swift 4 version:
let customView = UIView(frame: CGRect(x: 0, y: self.view.frame.size.height - 300, width: self.view.frame.size.width, height: 300))
customView.backgroundColor = UIColor.red
customView.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
UIApplication.shared.windows.last?.addSubview(customView)
The trick is to add the customView as a top subview to the UIWindow that holds the keyboard - and it happens to be the last window in UIApplication.shared.windows.
Swift 4.0
let customView = UIView(frame: CGRect(x: 0, y: self.view.frame.size.height-300, width: self.view.frame.size.width, height: 300))
customView.backgroundColor = UIColor.red
customView.layer.zPosition = CGFloat(MAXFLOAT)
let windowCount = UIApplication.shared.windows.count
UIApplication.shared.windows[windowCount-1].addSubview(customView)
As Tamás Sengel said, Apple's guidelines does not support adding a view over the keyboard. The recommended way to add a view over keyboard in Swift 4 & 5 is:
1) Add view with your "Next" button in your storyboard as external view and connect in your class (see Explain Image), in my case:
IBOutlet private weak var toolBar: UIView!
2) For the textfield you want to add your custom view over keyboard, add it as accessory view in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
phoneNumberTextField.inputAccessoryView = toolBar
}
3) Add action for "Next" button:
#IBAction func nextButtonPressed(_ sender: Any) {
descriptionTextView.becomeFirstResponder()
// or -> phoneNumberTextField.resignFirstResponder()
}
Explain Image:
Method 2: Result with image
In TableView Controller - add stricked view at bottom
Please follow this great link to handle safe area for screens like iPhone X if you want to use this method(2). Article: InputAccessoryView and iPhone X
override var inputAccessoryView: UIView? {
return toolBar
}
override var canBecomeFirstResponder: Bool {
return true
}
Do you have find some effective method to solve this problem? In iOS9,you put your customView on the top of the windows:
UIApplication.sharedApplication().windows[windowCount-1].addSubview(customView);
But if the keyboard dismisses, the top Windows will be removed, so your customView will be removed.
Looking forward for your help!
Thank you for your help!
You can definitely add the view to your application’s window, and you can also add another window entirely. You can set its frame and level. The level could be UIWindowLevelAlert.
While this can be possible with accessing the topmost window, I would avoid doing this, as it clearly interferes with Apple's guidelines.
What I would do is dismissing the keyboard and replacing its frame with a view with same dimensions.
The keyboard's frame can be accessed from keyboard notifications listed here, their userInfo contain a key that can be accessed with UIKeyboardFrameEndUserInfoKey.

Resources