UITextField beginningOfDocument property returning nil - ios

I have created a UITextField programatically. Now when I try to get its beginningOfDocument property, it is becoming nil.
Code is as follows:
public func addTextField(title:String?=nil) -> UITextField {
kWindowHeight -= 80.0
let txt: UITextField = UITextField(frame: CGRect(x: 13, y: 67, width: 213.00, height: 35));
txt.addTarget(self, action: "textFieldDidChange", forControlEvents: UIControlEvents.EditingChanged)
txt.borderStyle = UITextBorderStyle.RoundedRect
txt.layer.cornerRadius = 3
txt.layer.borderColor = UIColorFromRGB(0x1E8ADB).CGColor
txt.font = UIFont(name: kDefaultFont, size: 14)
txt.autocapitalizationType = UITextAutocapitalizationType.Words
txt.clearButtonMode = UITextFieldViewMode.WhileEditing
txt.layer.masksToBounds = true
txt.layer.borderWidth = 1.0
if title != nil {
txt.placeholder = title!
}
contentView.addSubview(txt)
return txt
}
In some other class I am calling:
let textField = AlertView().addTextField("Send SMS")
var begining = textField.beginingOfDocument

If you merely create an UITextField then immediately try to access beginningOfDocument property OR even after trying to add it to a superview, you will always get an uninitialized value.
Try to retrieve that property after the UITextField becomes the first responder and see the result :-)
Good luck.

Related

unable to get proper textfield with tag while looping through uitextfield class in swift?

for subviews in self.profilescroll.subviews
{
if ((subviews is UITextField) && (subviews.tag == 0))
{
let btntext : UITextField = subviews as! UITextField
btntext.resignFirstResponder()
}
if ((subviews is UITextField) && (subviews.tag == 1))
{
let btntext : UITextField = subviews as! UITextField
btntext.resignFirstResponder()
}
}
this is where i put tag to the textfield.
for i in 0 ..< 2
{
btnbudget = UITextField()
btnbudget.frame = CGRect(x: (CGFloat(i) * (((screenWidth - 160)/2) + 40)) + 40, y: 0, width: (screenWidth - 160)/2, height: 50)
btnbudget.font = UIFont(name: "Metropolis-SemiBold", size: 18)
btnbudget.layer.cornerRadius = 10.0
btnbudget.layer.borderWidth = 1.0
btnbudget.layer.borderColor = UIColor(hexString:"4B45A0").cgColor
btnbudget.tag = i
btnbudget.backgroundColor = UIColor(hexString: "#00000000")
btnbudget.text = budgetlist.object(at: i) as? String
btnbudget.textColor = UIColor(hexString:"4B45A0")
btnbudget.textAlignment = .center
btnbudget.delegate = self
budgetbtnview.addSubview(btnbudget)
}
where profilescroll is the view with 3 textfields in which i need to find textfield with tag values ranging from 0 to 1. but for some reason i am not getting the right one hence the keyboard is not returning.
You have assigned tag number to text field than don't need to loop over all subviews.
textField.tag = 101
You can directly use tag number to get view like below...
if let txtField = self.view.viewWithTag(101) as? UITextField {
print(txtField.text)
}
Make sure all the tag you give to view should be unique otherwise you may get unwanted view.
given tag should be unique ...try this..
for i in 100 ..< 102
{
btnbudget = UITextField()
btnbudget.frame = CGRect(x: (CGFloat(i) * (((screenWidth - 160)/2) + 40)) + 40, y: 0, width: (screenWidth - 160)/2, height: 50)
btnbudget.font = UIFont(name: "Metropolis-SemiBold", size: 18)
btnbudget.layer.cornerRadius = 10.0
btnbudget.layer.borderWidth = 1.0
btnbudget.layer.borderColor = UIColor(hexString:"4B45A0").cgColor
btnbudget.tag = i
btnbudget.backgroundColor = UIColor(hexString: "#00000000")
btnbudget.text = budgetlist.object(at: i) as? String
btnbudget.textColor = UIColor(hexString:"4B45A0")
btnbudget.textAlignment = .center
btnbudget.delegate = self
budgetbtnview.addSubview(btnbudget)
}
and ....
for subviews in self.profilescroll.subviews
{
if ((subviews is UITextField) && (subviews.tag == 100))
{
let btntext : UITextField = subviews as! UITextField
btntext.resignFirstResponder()
}
if ((subviews is UITextField) && (subviews.tag == 101))
{
let btntext : UITextField = subviews as! UITextField
btntext.resignFirstResponder()
}
}
but self.view.viewWithTag(101) approach is more appropriate than this
Let say you have three UITextField and you have set the tags as 0,1,2
Now, you can simply loop through all the Subview of your profilescroll
for case let textField as UITextField in self.profilescroll.subviews {
if textField.tag == 0 {
// do something
return
}
else if textfield.tag == 1 {
// do something
return
}
else if textfield.tag == 2 {
// do something
return
}
}
This will also ignore cases where your subview is not an UITextField thereby saving time.

How to center placeholder text in UISearchBar iOS 11

With iOS 11, searchBars are defaulting to a left aligned text. While this looks good with the rest of the native changes to iOS, it doesn't really fit my design, and I would like it to be center, as it was before.
I can't find any such alignment attributes on UISearchBar. Am I missing something, or is it simply not possible? Do I have to create my own custom search bar e.g derived from a UITextField to achieve this?
I had exactly the same problem - I'm struggling to understand why the default alignment would be changed without allowing us to easily set this back to centered.
The below works for me (Swift):
let placeholderWidth = 200 // Replace with whatever value works for your placeholder text
var offset = UIOffset()
override func viewDidLoad() {
offset = UIOffset(horizontal: (searchBar.frame.width - placeholderWidth) / 2, vertical: 0)
searchBar.setPositionAdjustment(offset, for: .search)
}
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
let noOffset = UIOffset(horizontal: 0, vertical: 0)
searchBar.setPositionAdjustment(noOffset, for: .search)
return true
}
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
searchBar.setPositionAdjustment(offset, for: .search)
return true
}
This is the only one that worked for me, Swift 4.2:
extension UISearchBar {
func setCenteredPlaceHolder(){
let textFieldInsideSearchBar = self.value(forKey: "searchField") as? UITextField
//get the sizes
let searchBarWidth = self.frame.width
let placeholderIconWidth = textFieldInsideSearchBar?.leftView?.frame.width
let placeHolderWidth = textFieldInsideSearchBar?.attributedPlaceholder?.size().width
let offsetIconToPlaceholder: CGFloat = 8
let placeHolderWithIcon = placeholderIconWidth! + offsetIconToPlaceholder
let offset = UIOffset(horizontal: ((searchBarWidth / 2) - (placeHolderWidth! / 2) - placeHolderWithIcon), vertical: 0)
self.setPositionAdjustment(offset, for: .search)
}
}
Usage:
searchBar.setCenteredPlaceHolder()
Result:
This is a bit of a workaround.
First of all you need to get the text field of the search bar and center the text alignment:
let textFieldOfSearchBar = searchController.searchBar.value(forKey: "searchField") as? UITextField
textFieldOfSearchBar?.textAlignment = .center
After that, you need to change the placeholder's alignment. For some reason it doesn't change with the text field's alignment. You should do that by adding a padding to the left view of the textfield only when the search controller is active, so use it's delegate methods:
func presentSearchController(_ searchController: UISearchController) {
//Is active
let width: CGFloat = 100.0 //Calcualte a suitable width based on search bar width, screen size, etc..
let textfieldOfSearchBar = searchController.searchBar.value(forKey: "searchField") as? UITextField
let paddingView = UIView(x: 0, y: 0, w: width, h: searchController.searchBar.frame.size.height)
textfieldOfSearchBar?.leftView = paddingView
textfieldOfSearchBar?.leftViewMode = .unlessEditing
}
func willDismissSearchController(_ searchController: UISearchController) {
let textfieldOfSearchBar = searchController.searchBar.value(forKey: "searchField") as? UITextField
textfieldOfSearchBar?.leftView = nil
}
Good luck
There's no official way to do that. Try using UISearchBarDelegate methods and your own UILabel.
extension YourViewController: UISearchBarDelegate {
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
placeholderLabel.isHidden = true
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
placeholderLabel.isHidden = false
}
}
Don't forget to hide the standard left-aligned icon (use blank image):
searchBar.setImage(UIImage.imageWithColor(.clear), for: .search, state: .normal)
let placeHolderOffSet = UIOffset(horizontal: 100, vertical: 0)
setPositionAdjustment(placeHolderOffSet, for: .search)
if you want a different position while the bar is active, you'll have to reset this in the corresponding delegate method
For swift 4.0+
Well as your question doesn't really specify only placeholder I'll give an answer to both placeholder and the text. In my project, I needed both texts to be centered at all times, not just when I was not editing the textfield. You can return it to left align with some of the answers in this post by using the delegate as they stated.
Also im not using SearchController, but a SearchBar outlet, either way it can be easily fixed for your project if you use a controller. (just replace for searchController.searchBar instead of just searchBar).
So just have to call the next function where you need it.
func searchBarCenterInit(){
if let searchBarTextField = searchBar.value(forKey: "searchField") as? UITextField {
//Center search text
searchBarTextField.textAlignment = .center
//Center placeholder
let width = searchBar.frame.width / 2 - (searchBarTextField.attributedPlaceholder?.size().width)!
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: searchBar.frame.height))
searchBarTextField.leftView = paddingView
searchBarTextField.leftViewMode = .unlessEditing
}
}
I did some modification in #Danny182 answer. Here is the updated version and it will work on all OS versions.
extension UISearchBar {
func setPlaceHolder(text: String?) {
self.layoutIfNeeded()
self.placeholder = text
var textFieldInsideSearchBar:UITextField?
if #available(iOS 13.0, *) {
textFieldInsideSearchBar = self.searchTextField
} else {
for view : UIView in (self.subviews[0]).subviews {
if let textField = view as? UITextField {
textFieldInsideSearchBar = textField
}
}
}
//get the sizes
let searchBarWidth = self.frame.width
let placeholderIconWidth = textFieldInsideSearchBar?.leftView?.frame.width
let placeHolderWidth = textFieldInsideSearchBar?.attributedPlaceholder?.size().width
let offsetIconToPlaceholder: CGFloat = 8
let placeHolderWithIcon = placeholderIconWidth! + offsetIconToPlaceholder
let offset = UIOffset(horizontal: ((searchBarWidth / 2) - (placeHolderWidth! / 2) - placeHolderWithIcon), vertical: 0)
self.setPositionAdjustment(offset, for: .search)
}
}
You just need to call:
searchBar.setPlaceHolder(text: "Search \(name)")

Looping an object in Swift

I'm having a problem with looping an object in Swift.
I want to declare the UITextfield as an instance variable so I can access it anywhere inside the class.
So if I put it inside the loop it works fine, but I put the variable of UITextField outside the loop, it doesn't show the first textfield in the view and the username textfield would be missing.
Here's my code
var placeHolderArr:[String] = ["Username", "Password"]
let loginField = UITextField() // <----- this one
override func viewDidLoad() {
super.viewDidLoad()
let logoHolder = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height / 2))
logoHolder.backgroundColor = self.utils.hexStringToUIColor(hex: "#ff8f00")
for index in 0...1 {
let height = index * 60
// When I declare the textfield variable in this line, it works fine.
loginField.frame = CGRect(x: 0 , y: Int((logoHolder.frame.maxY + 30) + CGFloat(height)), width: Int(self.view.frame.width - 80), height: 50)
loginField.placeholder = placeHolderArr [index]
loginField.font = UIFont.systemFont(ofSize: 15)
loginField.borderStyle = UITextBorderStyle.roundedRect
loginField.autocorrectionType = UITextAutocorrectionType.no
loginField.keyboardType = UIKeyboardType.default
loginField.returnKeyType = UIReturnKeyType.done
loginField.clearButtonMode = UITextFieldViewMode.whileEditing;
loginField.contentVerticalAlignment = UIControlContentVerticalAlignment.center
loginField.center.x = self.view.center.x
loginField.delegate = self
loginField.tag = index
innerY = Int(loginField.frame.maxY)
print("index\(index)" )
self.view.addSubview(loginField)
}
}
Please see the screenshot below
Textfield when declared inside the loop:
Textfield when declared outside the loop:
Obviously you use the same UITextField. Judt put initialization inside the loop
for index in 0...1 {
let height = index * 60
// When I declare the textfield variable in this line, it works fine.
loginField = UITextField()
loginField.frame = CGRect(x: 0 , y: Int((logoHolder.frame.maxY + 30) + CGFloat(height)), width: Int(self.view.frame.width - 80), height: 50)
loginField
And to use it later use array:
var fieldArr:[UITextField] = []
Then later:
fieldArr.append(loginField)
Summarise:
var placeHolderArr:[String] = ["Username", "Password"]
let loginFields:[UITextField] = []
override func viewDidLoad() {
super.viewDidLoad()
let logoHolder = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height / 2))
logoHolder.backgroundColor = self.utils.hexStringToUIColor(hex: "#ff8f00")
for index in 0...1 {
let loginField = UITextField()
let height = index * 60
// When I declare the textfield variable in this line, it works fine.
loginField.frame = CGRect(x: 0 , y: Int((logoHolder.frame.maxY + 30) + CGFloat(height)), width: Int(self.view.frame.width - 80), height: 50)
loginField.placeholder = placeHolderArr [index]
loginField.font = UIFont.systemFont(ofSize: 15)
loginField.borderStyle = UITextBorderStyle.roundedRect
loginField.autocorrectionType = UITextAutocorrectionType.no
loginField.keyboardType = UIKeyboardType.default
loginField.returnKeyType = UIReturnKeyType.done
loginField.clearButtonMode = UITextFieldViewMode.whileEditing;
loginField.contentVerticalAlignment = UIControlContentVerticalAlignment.center
loginField.center.x = self.view.center.x
loginField.delegate = self
loginField.tag = index
innerY = Int(loginField.frame.maxY)
print("index\(index)" )
self.view.addSubview(loginField)
loginFields.append(loginField)
}
}
When you declare the UITextfield subclass outside your loop, than every time when you set it up, you set up the same UITextfield instance. This means, you are overriding the first set of properties with the next one, every time you iterate on it.
Instead, lets create two instances, and set up both independently:
// declare this outside your loop
let usernameField = UITextField()
let passwordField = UITextField()
for (index, loginField) in [usernameField, passwordField].enumerated() {
// do your setup
}
You can create two properties:
let usernameField = UITextField()
let passwordField = UITextField()
and loop every text field like:
for (index, loginField) in [usernameField, passwordField].enumerated() {
//do what you need
}

change text of UILabel with timer

i want to change the text of a UILabel after a certain amount of time. but the text isn't changed after the set amount of time. how do i fix this?
see my code:
var countDownText = "hello"
override func didMoveToView(view: SKView) {
startButton = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 90))
startButton.text = "\(countDownText)"
startButton.center = CGPointMake(view.frame.size.width / 2, view.frame.size.height/2)
startButton.textColor = UIColor.darkGrayColor()
startButton.font = UIFont(name: "Arial", size: 20)
startButton.textAlignment = NSTextAlignment.Center
self.view?.addSubview(startButton)
countDownTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("countDownFunc"), userInfo: nil, repeats: true)
}
func countDownFunc(){
theTime++
if(theTime >= 4){
countDownText = "testText"
}
if(theTime >= 8){
spawnEnemys()
startButton.removeFromSuperview()
countDownTimer.invalidate()
}
print(theTime)
}
thanks in advance for all your help :D
Your countDownFunc should be:
func countDownFunc(){
theTime++
if(theTime >= 4){
countDownText = "testText"
startButton.text = countDownText
}
if(theTime >= 8){
spawnEnemys()
startButton.removeFromSuperview()
countDownTimer.invalidate()
}
print(theTime)
}
There's a design flaw in your code.
You change the string assigned to the view controller's countDownText property, but that does not also change the label's current text.
Here's a simple playground example to illustrate the problem:
import UIKit
var str = "Hello, playground"
var label = UILabel()
label.text = str
str = "Goodbye, playground"
print(label.text) // "Hello, playground"
If you also want to update the label's text, you need to update its text property, similar to what you initially did:
startButton.text = "\(countDownText)"
This will update the label's text to match the countDownText property's new value.

How to add and delete textfields on a view when click on add and delete buttons in Swift?

I am able to add a textfield on button click but i am unable to delete the textfields that i have added, i am only able to delete the recent textfield that i have added but unable to delete the other previous textfields, how can I delete/remove those textfields in Swift.
I have successfully added the buttons but i am unable to delete those textfields.
I have tried to get those tag values for each textfield but here I am missing some logic please help in this regard. Thanks in advance.
Below is the image for reference when i click on delete(-) button then with respect to that textfield must be removed not all the subviews.
// for add method
#IBAction func btnAddOptionAction(sender: UIButton)
{
txtOption = UITextField(frame: CGRectMake(52, yFrame+17, 195, 30))
txtOption.borderStyle = .None
txtOption.tag = index
let modifiedURLString = NSString(format:"Option %d", index) as String
//textField.text = modifiedURLString
txtOption.attributedPlaceholder = NSAttributedString(string:modifiedURLString,
attributes:[NSForegroundColorAttributeName: UIColor.whiteColor()])
txtOption.font = UIFont(name: "Avenir Medium", size: 16)
txtOption.textColor = UIColor.whiteColor()
self.scrOptions.addSubview(txtOption)
btnDeleteOption.frame = CGRectMake(259, txtOption.frame.origin.y + 8, 25, 25)
btnAddOption.frame = CGRectMake(259, txtOption.frame.origin.y + txtOption.frame.size.height + 10, 25, 25)
btnDeleteOption.hidden = false
print("Add index", index)
index++
yFrame = yFrame + 40
scrOptions.contentSize = CGSizeMake(0, btnAddOption.frame.origin.y + btnAddOption.frame.size.height + 20)
}
//For delete method
#IBAction func btnDeleteOptionAction(sender: UIButton)
{
yFrame = yFrame - 40
index--
sender.tag = index
txtOption.removeFromSuperview()
imgoption.removeFromSuperview()
imgUnderLine.removeFromSuperview()
btnDeleteOption.frame = CGRectMake(259, yFrame - 15, 25, 25)
btnAddOption.frame = CGRectMake(259, yFrame + 32 - 15, 25, 25)
if index <= 1
{
btnDeleteOption.frame = CGRectMake(259, 25, 25, 25)
btnAddOption.frame = CGRectMake(259, 25, 25, 25)
btnDeleteOption.hidden = false
}
print("y-axis ",btnDeleteOption.frame.origin.y)
}
What about:
var myTag = 0
if let myView = UIView() {
view.addSubview(myView)
myView.tag = myTag++
}
if let viewWithTag = self.view.viewWithTag(myTag) {
viewWithTag.removeFromSuperview()
}
It must remove the last element
Try removing all subviews.
After removing all subviews add required textfield in the view.
Hope it helps.. Happy Coding.. :)
#IBOutlet weak var updateIdView: UIView!
let textField: UITextField = updateIdView.viewWithTag(textFieldTag) as! UITextField
textField.removeFromSuperview()

Resources