First time to add text to textField [Swift] - swift

I think it is something really easy but I am a noob and I can't figure out what to do, So I am trying to check if the TextField has nothing inside it, it would let the user write something in it, but if it has some text saved (I have saved the data using NSUserDefaults) in there when you quit the app it will remain the text that was typed in that TextField.
I had accomplished this before but something went wrong and stopped working.
Here is what I have done:
var notTheFirstTime = false
if notTheFirstTime == true{
let mail = NSUserDefaults.standardUserDefaults().objectForKey("usersMailID") as! String
textField.text = "\(mail)"
}
Here is the part where the text filed is saved and change the status of the "notTheFirstTime" to true
func textFieldShouldReturn(textField: UITextField) -> Bool {
performAction()
return true
}
func performAction() {
notTheFirstTime = false
NSUserDefaults.standardUserDefaults().setObject(textField.text!, forKey: "usersMailID")
NSUserDefaults.standardUserDefaults().synchronize()
print("Saved!!!!!!")
saveLabel.text! = "Saved!"
savedIcon.image = UIImage(named: "Saved")
}

I see two things in this code, After the text saved in to user default you should use
notTheFirstTime = true
but you set this as false in performAction()
Second one is you should use notTheFirstTime value also as a User defaults. When you close the app. This value also rest to the false( as you given on initial step of the application) So use it's value also as a user defaults.

Related

How to hide the last digit while writing a password in swift

Recently I've been working on a UITextField that hides the password while editing text on the loginPage, password field.
As everyone knows .isSecureTextEntry = true solves the problem up to a point. However, while typing the password, the last character appears and is perceived as a security hole in a way. So I could not find out a solution how to solve this. Thank you in advance for your answers.
I attached two pictures.
The solution offered by UIKIT today.
textField.isSecureTextEntry = true
It's what I wanted.
I want this
When ever you input the character the func textField(_:shouldChangeCharactersIn:replacementString:) will call to replace old text with new text. So you just need to catch the value and don't let it update by default.
textField.isSecureTextEntry = true
textField.delegate = self // add delegate to catch the action
Get the value when changing in your TextField and don't get it update the UI automatically.
extension ViewController : UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let currentText = self.textField.text as? NSString {
let newText = currentText.replacingCharacters(in: range, with: string) // change to new one
self.textField.text = newText // assign it back to textField text
return false // don't let it update automically
}
return true // by default
}
}
If you have multiple textField in the screen you must checked if the current changing is your password one before. If yes return false else return true like default.

NSTextField keep focus/first responder after NSPopover

The object of this app is to ensure the user has entered a certain text in an NSTextField. If that text is not in the field, they should not be allowed to leave the field.
Given a macOS app with a subclass text field, a button and another generic NSTextField. When the button is clicked, an NSPopover is shown which is 'attached' to the field which is controlled by an NSViewController called myPopoverVC.
For example, the user enters 3 in the top field and then clicks the Show Popover button which displays the popover and provides a hint: 'What does 1 + 1 equal'.
Note this popover has a field labelled 1st resp so when the popover shows, that field becomes the first responder. Nothing will be entered at this time - it's just for this question.
The user would click the Close button, which closes the popover. At that point what should happen if the user clicks or tabs away from the field with the '3' in it, the app should not permit that movement - perhaps emitting a Beep or some other message. But what happens when the popover closes and the user presses Tab
Even though that field with the '3' in it had a focus ring, which should indicate the first responder again in that window, the user can click or tab away from it as the textShouldEndEditing function is not called. In this case, I clicked the close button in the popover, the '3' field had a focus ring and I hit tab, which then went to the next field.
This is the function in the subclassed text field that works correctly after the text has been entered into the field. In this case, if the user types a 3 and then hits Tab, the cursor stays in that field.
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if self.aboutToShowPopover == true {
return true
}
if let editor = self.currentEditor() { //or use the textObject
let s = editor.string
if s == "2" {
return true
}
return false
}
The showPopover button code sets the aboutToShowPopover flag to true which will allow the subclass to show the popover. (set to false when the popover closes)
So the question is when the popover closes how to return the firstResponder status to the original text field? It appears to have first responder status, and it thinks it has that status although textShouldEndEditing is not called. If you type another char into the field, then everything works as it should. It's as if the window's field editor and the field with the '3' in it are disconnected so the field editor is not passing calls up to that field.
The button calls a function which contains this:
let contentSize = myPopoverVC.view.frame
theTextField.aboutToShowPopover = true
parentVC.present(myPopoverVC, asPopoverRelativeTo: contentSize, of: theTextField, preferredEdge: NSRectEdge.maxY, behavior: NSPopover.Behavior.applicationDefined)
NSApplication.shared.activate(ignoringOtherApps: true)
the NSPopover close is
parentVC.dismiss(myPopoverVC)
One other piece of information. I added this bit of code to the subclassed NSTextField control.
override func becomeFirstResponder() -> Bool {
let e = self.currentEditor()
print(e)
return super.becomeFirstResponder()
}
When the popover closes and the textField becomes the windows first responder, that code executes but prints nil. Which indicates that while it is the first responder it has no connection to the window fieldEditor and will not receive events. Why?
If anything is unclear, please ask.
Here's my attempt with help from How can one programatically begin a text editing session in a NSTextField? and How can I make my NSTextField NOT highlight its text when the application starts?:
The selected range is saved in textShouldEndEditing and restored in becomeFirstResponder. insertText(_:replacementRange:) starts an editing session.
var savedSelectedRanges: [NSValue]?
override func becomeFirstResponder() -> Bool {
if super.becomeFirstResponder() {
if self.aboutToShowPopover {
if let ranges = self.savedSelectedRanges {
if let fieldEditor = self.currentEditor() as? NSTextView {
fieldEditor.insertText("", replacementRange: NSRange(location: 0, length:0))
fieldEditor.selectedRanges = ranges
}
}
}
return true
}
return false
}
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if super.textShouldEndEditing(textObject) {
if self.aboutToShowPopover {
let fieldEditor = textObject as! NSTextView
self.savedSelectedRanges = fieldEditor.selectedRanges
return true
}
let s = textObject.string
if s == "2" {
return true
}
}
return false
}
Maybe rename aboutToShowPopover.
If you subclass each of your NSTextField, you could override the method becomeFirstResponder and make it send self to a delegate class you will create, that will keep a reference of the current first responder:
NSTextField superclass:
override func becomeFirstResponder() -> Bool {
self.myRespondersDelegate.setCurrentResponder(self)
return super.becomeFirstResponder()
}
(myRespondersDelegate: would optionally be your NSViewController)
Note: do not use the same superclass for your alerts TextFields and ViewController TextFields. Use this superclass with added functionality only for TextFields you would want to return to firstResponder after an alert is closed.
NSTextField delegate:
class MyViewController: NSViewController, MyFirstResponderDelegate {
var currentFirstResponderTextField: NSTextField?
func setCurrentResponder(textField: NSTextField) {
self.currentFirstResponderTextField = textField
}
}
Now, after your pop is dismissed, you could in viewWillAppear or create a delegate function that will be called on a pop up dismiss didDismisss (Depends how your pop up is implemented, I will show the delegate option)
Check If a TextField has existed, and re-make it, the firstResponder.
Pop up delegate:
class MyViewController: NSViewController, MyFirstResponderDelegate, MyPopUpDismissDelegate {
var currentFirstResponderTextField: NSTextField?
func setCurrentResponder(textField: NSTextField) {
self.currentFirstResponderTextField = textField
}
func didDismisssPopUp() {
guard let isLastTextField = self.currentFirstResponderTextField else {
return
}
self.isLastTextField?.window?.makeFirstResponder(self.isLastTextField)
}
}
Hope it works.
Huge thanks to Willeke for the help and an answer that lead to a pretty simple solution.
The big picture issue here was that when the popover closed, the 'focused' field was the original field. However, it appears (for some reason) that the windows field editor delegate disconnected from that field so functions such as control:textShouldEndEditing were not being passed to the subclassed field in the question.
Executing this line when the field becomes the first reponder seems to re-connect the windows field editor with this field so it will receive delegate messages
fieldEditor.insertText("", replacementRange: range)
So the final solution was a combination of the following two functions.
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
if self.aboutToShowPopover == true {
return true
}
let s = textObject.string
if s == "2" {
return true
}
return false
}
override func becomeFirstResponder() -> Bool {
if super.becomeFirstResponder() == true {
if let myEditor = self.currentEditor() as? NSTextView {
let range = NSMakeRange(0, 0)
myEditor.insertText("", replacementRange: range)
}
return true
}
return false
}

Why is my value only being set after the app is opened and closed for the first time in swift?

I have a value, showAds, that determines wether or not the user has purchased noAds or not. If it is true then ads are displayed. It works for the most part, except for when the user opens the app for the first time it does not show ads. If they close out the app and open it again, then ads show as they should. How do I fix this? Thank you, any help is appreciated!
Here is all of the code associated with that value:
var showAds = UserDefaults.standard.bool(forKey: "showAds")
func adViewDidReceiveAd(_ bannerView: GADBannerView) {
if showAds == true {
view.addSubview(bannerView)
}
}
override func viewDidLoad() {
super.viewDidLoad()
UserDefaults.standard.set(true, forKey: "showAds")
let userDefaults = UserDefaults.standard
let appDefaults = ["showAds" : true]
userDefaults.register(defaults: appDefaults)
print("SHOWING ADS: \(showAds)")
if showAds == true {
self.view?.addSubview(bannerView)
}
if showAds == false {
bannerView.removeFromSuperview()
}
}
You are using the stored property with the default value which is being assigned at the moment of your view controller's initialization. The viewDidLoad method is called after that (more info about the view controller lifecycle here).
Since at the first launch of your app your user defaults are empty, your stored property is set to "false" and will keep this value until you change it directly.
If you want to have a property which will be fully dependent on your user defaults value, the best choice is a computed property:
var showAds: Bool {
return UserDefaults.standard.bool(forKey: "showAds")
}
[...] computed properties do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.
More info about different property types in swift here

Enable blocked rows after in app purchase. Coding issue

I have an app that blocks User access to a few rows of a view controller. This is done by checking if a variable of type bool is set to true or false.
var unlocked: bool = false
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
//blocking cells if they are not paid for.
if unlocked == false {
if ( indexPath.row >= 2 ) {
cell.userInteractionEnabled = false
cell.contentView.alpha = 0.5
}
else{
cell.userInteractionEnabled = true
cell.contentView.alpha = 1
}
}
return cell
}
This works perfectly. I then have an option for the user to purchase access to the remaining rows, and hence the remaining content of the app. Once the In-app-purchase has been purchased it will run the function "updateSections()". I know this function is called upon purchase as I have tested it.
I now want to allow the user access to the remaining rows in the table view from the "updatedSections()" function as they will have paid for it.
What i have tried is:
//function to unlock
func unlockSections() {
//This is the code for what happens once the device has bought the IAP. going to have to save what happens here in using nsuserdefaults to make sure it will work when the app opens and closes.
print("The IAP worked")
let unlocked = true
tableview.reloadData()
}
However this does not seem to work. I can't see where I am going wrong.
The problem is that this line:
let unlocked = true
is defining a new constant called unlocked which only exists in the scope of your unlockSections method. It is quite separate from the property called unlocked which is defined at the start of your class. To update the property instead of creating the new constant, just drop the "let":
unlocked = true
or if you want to be crystal clear (or you want to have both, but use the property in a specific case), use "self." to emphasise that you are intending to use the property:
self.unlocked = true

Swift Storing information in a constant and keeping it there

Hello I am currently doing a project where the user types something in a textfield, and presses a button, where whatever the user types in the textfield appears as part of the alertbody of a notification.
To do this I save the textfield.text as a variable and then use string interpolation to include it into the alertbody.
The problem is that the time in which I fire the notifications change every week, so I have to make new notifications. And I still want to use whatever the user typed into the textfield. But since the textfield.text was saved inside a function, I do not think that it will be stored outside the function.
This is what my code looks like so far
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(604800, target: self,selector:Selector("repeater"), userInfo: nil, repeats: true)
}
savedtext:String!
#IBAction func setNotification{
textfield.text = savedtext
//set notifications and other stuff here
}
//outside the function savedtext should not have the textfield's info
func repeater{
//setting notification
}
Sorry if my question is a bit difficult to understand
Any help is appreciated
I'd use NSUserDefaults here, it will persist your data in a key-value map, even after the user or system kills the app. The code would look like this
let ALERT_MESSAGE_KEY : String = "myAlertMsgKey"
#IBAction func setNotification{
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setValue(textfield.text , forKey: ALERT_MESSAGE_KEY)
NSUserDefaults.standardUserDefaults().synchronize()
//set notifications and other stuff here
}
and in repeater function:
func repeater {
let defaults = NSUserDefaults.standardUserDefaults()
let alertMessage = defaults.valueForKey(ALERT_MESSAGE_KEY) as? String ?? "No message saved"
}
You could also use Core Data for this, but it would be an overkill for something so simple