I have Swift object with about 20 Properties. In the app, there is a screen to get the user input and create the above swift object from the user entered value. Right now, if the user clicks the back button, all the user entered data will lose. So I want to alert the user to save the details if he/she has made any changes. How do we identify if the user has made any changes to the properties. Is it possible to use KVO in this case as we have too many properties?
What you need is a data model to hold the information in that particular screen, and then compare it with the original data when leaving the screen.
For the sake of simplicity, let's assume your screen has 2 text fields. One holds a name and another the age of a person.
struct Person: Equatable {
var name: String
var age: Int
}
When you first open this screen, the model will have the default values. Create a copy of this model and whenever the user makes any changes to the values on the screen, update the copy.
class YourViewController: UIViewController {
// Populate these 2 values when creating your view controller
var person: Person!
var personCopy: Person!
.
.
.
// You need to add this target to your text fields
#objc func textFieldDidChange(_ textField: UITextField) {
switch textField {
case personTextField:
personCopy.name = personTextField.text!
case ageTextField:
personCopy.age = Int(ageTextField.text!)!
default:
// Handle other text fields here or write separate cases for them
}
func dismissView() {
if person == personCopy {
// Dismiss your view
} else {
// Show alert
}
}
}
If the user presses the back button, all you need to do is compare these 2 models and check if they are the same. If they are the same, you can go back, if not, prompt an alert asking the user to save changes or discard.
I think KVO would be overkill here. Use KVO only for distant objects in your app.
Here you have the UITextFields in your viewController and you must have reference to the user object anyway.
Easier is: On the back button press you would check all text properties of your UITextField objects to the (existing) values of your user object. If one of them has changed then present the alert.
Related
So I just did a bit of research on UserDefaults last night and this morning and I wanna know how I can use dictionaries in a certain way. My issue is that when a user presses a certain button and the UserDefaults dictionary data gets set, every piece of data in the app is affected as well. My goal is to just have that piece of data affected only and the rest stay the same as they were before.
let eventDict = ["eventName": "\(selectedEventName!)", "purchased": true] as [String : Any]
This is the dictionary I set, pretty simple. And when the button is pressed I run this line of code.
self.defaults.set(eventDict, forKey: "eventDict")
These work perfect and I check the .plist file and everything is correct, it shows the event name and the purchased as 1 (true).
Now I tried to add some logic, in my viewDidLoad() of the page I purchase the event on and it works but when I check every other event the page has the same outcome, which is not what I want.
let checkEventStatus = defaults.dictionary(forKey: "eventDict")
if checkEventStatus?.isEmpty == false {
viewPurchaseButton.isHidden = false
cancelPurchaseButton.isHidden = false
purchaseTicketButton.isHidden = true
creditCard.isHidden = true
} else {
viewPurchaseButton.isHidden = true
cancelPurchaseButton.isHidden = true
}
I couldn't figure out how to retrieve an exact value from a key in a dictionary, so I just used isEmpty() instead which I thought would make sense because if the event wasn't purchased, it wouldn't have a dictionary with it's name in it.
These are the buttons I want to show up when the purchase button is pressed. The purchase button is hidden right now because it was already pressed, so the UserDefault data is set, and that effects the button visibility. Now when I check every other event, the screen is the same. I just want each screen to be a certain way depending on if the event is purchased or not using UserDefaults.
I think you have a fundamental problem - you're not actually saving any purchase status liked to an event in the first place, so there's no way you'll be able to determine the status for an event and set the button status accordingly. You need a data structure that associates the event name with the event status.
I think you'll need more thought into your data and workflow, but as a trival example that might get you started, you could create a Event struct to hold both name and status. You could also build in the purchase date and cost...
struct Event {
name: String
purchased: Bool
purchaseDate: Date?
cost: Double?
}
and then you could carry on using a dictionary in the form of [String: Event] where the key is some sort of string for event ID, but it'd probably be easier for now to work with an array [Event]
You could then iterate through the array showing all the events (or filter to get specific ones) and then set the screen up accordingly, for example if event is an item from the array
eventNameField.text = event.name
if purchased, let date = event.purchaseDate, let cost = event.cost {
purchasedDateField.text = "\(date)" //would really be a dateFormatted date
costFiled.text = "\(cost)"
}
purchaseButton.isHidden = event.purchased
cancelButton.isHidden = !event.purchased
I'm working on a project where I should save data locally with Core Data.
Here is my workflow :
To start, I ask the user to fill a form, let's say his firstname and lastname.
He clicks on the submit button, then data is saved on the device using Core Data
User is redirected to the "last filled form" view controller.
I have a bar button item that when clicked can show the latest filled form.
I should test if the array of filled forms is empty, then the button should be disabled.
Otherwise, the button should be enabled ...
I tried this piece of code, where I fetch data from the database and affected to an array but the button seams not working at all and it never gets disabled ...
class ViewController: UIViewController {
var userIdentity: UserIDentity = UserIDentity(context: PersistanceService.context)
var identityArray = [UserIDentity]()
override func viewDidLoad() {
super.viewDidLoad()
self.fetchIdentityHistoryArray()
}
func fetchIdentityHistoryArray(){
let fetchRequest: NSFetchRequest<UserIDentity> = UserIDentity.fetchRequest()
do {
let identityArray = try PersistanceService.context.fetch(fetchRequest)
if identityArray.isEmpty {
self.identityHistoryButton.isEnabled = false
}
else {
self.identityHistoryButton.isEnabled = true
}
}
catch {
print("Error fetching sworn statement history !")
}
}
}
So I have 2 questions :
What's wrong with my code ?
How can I manage that when the user clicks on the "back button" for the first form filled ever, the "history button" can refresh itself and turn from disabled to enabled button ?
Any help is much appreciated. Thank you
You are initialising a core data object directly in your code
var userIdentity: UserIDentity = UserIDentity(context: PersistanceService.context)
This new object will exist in Core Data and will be included everytime you execute the fetch request. You must understand that Core Data is not a database layer it is an object mapping layer so even if you haven't called save() yet the object exists in the Core Data context.
Either change the declaration to
var userIdentity: UserIDentity?
or remove it completely if it isn't used.
Let's say I have a textfield where I tell the user to put in their name. The 'name' then gets populated into all the other appropriate fields. For example, say the 'other field' is where the person has to sign a document. Therefore, underneath that 'line' where the person would have to sign the document, the persons name will then be populated there.
How would I go about to fill out information in one part and it would then be populated in all the appropriate fields?
You could implement UITextFieldDelegate and your delegate will be called when the user enter something in your text field.
First set the delegate to self
txtField.delegate = self
Use this if you want to change the other fields when the user is done editiing your master field
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
// change other fields here
otherField.text = textField.text
}
In the initial set up of my app I need the user to chose a genre type. i have it set up such that the user selects from a list of UIButtons which are all different genre types. I need this genre selection to save to the users profile. The next view controller is the same set up as the genreVC but asking for instrument instead of genre. The next segue from here is to view controller 3 the usersVC profile and it should have saved which two buttons were selected on the previous two VC's by updating a label. I have tagged each button with a number and have tried next to everything.
How do I save which button has been pressed on a different view controller to the users profile and update the label on the users profile to the name of the button pressed?
I am using each button individually and have them tagged as such as seen here:
#IBAction func popGenreButton(sender: UIButton) {
if(sender.tag == 6){
print("Pop genre selected")
}
}
#IBAction func altGenreButton(sender: UIButton) {
if(sender.tag == 7){
print("Alternative genre selected")
}
}
#IBAction func electronicGenreButton(sender: UIButton) {
if(sender.tag == 8){
print("Electronic genre selected")
}
}
I also tried adding each button into genreSelection as shown below that didn't work so I stuck with the individual buttons above.
#IBAction func genreSelectionButton(sender: UIButton) {
print(sender.titleLabel!.text!)
genreResult.text = genreString + " genre has been saved to your profile"
}
I also created a nib with myModalDelegate and protocol and nope.
I thought maybe I could create a genre array and when a certain button is selected it calls a certain item from the array and updates that item to the label but I was unsure how to go about this too.
I am using Xcode 7 and Swift 2.0 and Parse as my cloud server.
As you can see I have a lot to learn yet with Swift. I have been searching how to do this for the past week and can't figure it out. I am a 'lot' out of my depth but want to figure it out, if anyone can help that would be great!
A simple solution would be something like creating a global store for those values
i.e.
struct Choices {
static var genre: String?
static var instruments: String?
}
then you could in your different view controllers write and read from it by simply
Choices.instrument = "trumpet"
or
label.text = Choices.genre
A prettier solution would be to create a similar class as the previous struct but not have the variables static. You could then pass it along and fill up with the values from each VC and use the values in the last VC where you are setting the label text
I Need some help to understand how can I filter a information in Uitableview with UISegmentedControl. I have a UITabeView with data that contain two different data, Rec and Dat . I want to load ALL data when start application and separate Deb and Rec when user choose in UISegmentedControl. When I start application I populate 3 Array alls, recs and dats. I show the array alls, and want to change/filter the data when the user change the choose in UisegmentControl. Can you help me please ?
#IBAction func filtroDebitoCredito(sender: AnyObject) {
//when All
if FiltroControlerTable.selectedSegmentIndex == 0 {
// tableView.reloadData() ???
}
//When Creds
if FiltroControlerTable.selectedSegmentIndex == 1 {
// ???
}
//Debs
if FiltroControlerTable.selectedSegmentIndex == 2 {
// ???
}
Tks for help
Actually, you are already there. Your table view displays model data. So when the user changes the value of the segmented control, switch to the correct set of model data and, exactly as you say, tell the table to reloadData(). What I would do is have four arrays: model, all, recs, and dats. The table, let us say, always displays model. So the segmented control would copy, let us say, recs into model and tell the table view to reload!