I am creating a list and displaying it in a UITableView. While viewing I want to delete an item and then save the updated list, allowing me to refresh the old list with a refresh button. I use a button to save the current list, then a refresh button to reload the data into the view. The problem is the code within the save button always runs, even when I don't press the button. I can explain best like this:
btnSave.TouchUpInside += delegate { _ListCopy = new List<Tasks>();
};
btnReset.TouchUpInside += delegate {
tblTotal.ReloadData();
tblTotal.DataSource = new ListDataSource(_ListCopy,txtTotalCost);
};
tblTotal.DataSource = new ListDataSource (_ListOfTasks,txtTotalCost);
When I run this the table view shows the _ListOfTaskslist, when I press btnReset the _ListCopy is reloaded as the data source. The issue I am having is that the value for _ListCopy is assigned even when I don't press btnSave. In this case the view is cleared. If I comment out the line _listCopy = new List<Tasks>(); I receive a null reference exception in the table view Data Source here:
public override int RowsInSection (UITableView tableView, int section)
{ return _ListOfTasks.Count; } <-- exception
indicating to me that the value of _ListCopy is empty. I thought I could make _ListCopy = _ListOfTasks in place of new List. But, for some reason the code is running in the save button regardless, so I am never able to save the correct list value when I want it. Why is_ListCopy be assigned a value without telling it? I do not assign it a value anywhere else either. Can someone explain what is happening? Is there a different solution that I could use instead?
I discovered that I had my outlets setup wrong. For some reason I had two outlets wired to btnreset and no outlets for btnsave. So when I pressed reset, I was also firing off the code for btnsave. I solved the whole issue by correcting my outlets. As for making a copy of the original list: I was trying to simply make _ListCopy = _ListOfTasks for the save function. This only pointed to the memory location of _ListOfTasks, which included any changes. I corrected this by writing a foreach loop to copy one list to the other
foreach( var task in _ListOfTasks)
{
_ListCopy.Add(task);
}
Now I can press save and the first estimate is stored. I can do any deletions, and if the customer wants to revisit the initial estimate I can refresh the data.
Related
I'm building an app that is a list of homework.
I'm a sort of beginner so I'm not using custom classes to save it and neither using CoreData. Just using NSUserDefaults and passing the data inside arrays between the views. My project is almost done (I want to implement some stuff like animations and etc but first I need to get it fully working right?), but I have 2 silly problems to solve, but I can't do it alone!
If I was going to put the code here the question would be too long, so I will leave the link of the project for you who is helping me check out.
https://github.com/HenriqueDoura/Agenda Here it is!
So, the first problem:
as it is a homework list app, it will control if the app isPendent or if it !isPendent, for that, I've put a label to the custom AtivCell called pendenciaLbl (which means pendencyLbl in English). By default, the bool is initialized as true, cause if the user is adding a homework, it is obvious that he did not finish it yet, right?
To change the bool value to false I've added a new actionForRowAtIndexPath action, called Feito (which means Done in English). By pressing it, I make the isPendent value false. On the table view func
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
I've put an if/else statement:
if isPendent {
cell.pendenciaLbl.text = "Pendente"
cell.pendenciaLbl.textColor = UIColor.redColor()
} else {
cell.pendenciaLbl.text = "Concluída"
cell.pendenciaLbl.textColor = UIColor.greenColor()
}
Pendente means Pendent and Concluída means concluded.
The thing is that when I set the cell I'm on to Feita/Done, it is setting all the other cells as done too and changing all the pendenciaLbl.text to Concluded, which is obviously not what I want, I just want to set ONE cell as Done.
The second and last problem envolves the same boolean isPendent. I don't know how to save this value, cause if I set a cell as Done and relaunch the app (after killing it via the app switcher) the pendenciaLbl comes back to "Pendent". How can I save the value when a cell is set to "Concluded" and check if it was set before when I launch the app?
Sorry for all english mistakes, I'm Brazillian.
Right now you only have one variable for isPendent. So if one cell changes that variable, all the cells are changed.
What you'll need to do is create a Array of Bools, one for each cell. Just like you have arrays of strings for other data like material and describes.
var pendent = [Bool]()
Then in cellForRowAtIndexPath
if pendent[indexPath.row] {
cell.pendenciaLbl.text = "Pendente"
cell.pendenciaLbl.textColor = UIColor.redColor()
} else {
cell.pendenciaLbl.text = "Concluída"
cell.pendenciaLbl.textColor = UIColor.greenColor()
}
I have two steppers in a ViewController, one called homeStepper the other called awayStepper. When I run the app on my phone the value for homeStepper changes just fine, however nothing happens when I try to use the awayStepper. I added log statements and they just show the value of the stepper never changing. It always sits at 0 no matter how many times I push either button on it. Here is my code: https://github.com/Zach443/ScoreKeeper/blob/c83d5eeaadafaad4f093437679d26a8b55ef5dd8/ScoreKeeper/ViewController.swift
You have a typo:
approxAwayScore = homeStepper.value
Should be
approxAwayScore = awayStepper.value
I am newbie in iOS with swift. What I need to do right now is that I have a textfield for user to enter his/er username. And once he clicks the other textfields, say password section, the application will automatically check whether this name exists or not and display a "V" or "X" image in a imageView. But I don't know how to do that or what method or action I should deal with. In Android, I could detect the focus of that textfield.Once the textfield loses the focus and if the text isn't empty, I can retrieve the text and request to my server to verify whether it exists or not. In iOS, I'm totally confused how to detect this, and is this related with first responder? Thx for advice in advance!
Use UITextFieldDelegates.
class XXX : YOURCONTROLLER, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.PASSCODE.delegate = self
}
func textFieldDidEndEditing(textField: UITextField) {
if textField == PASSCODE {
//update stuffs
}
}
}
On iOS, you generally create the interface in Interface Builder (a graphical tool you can use to place UI elements on screen and define many of their properties), and then you link them to your code using what's called IBOutlets/IBActions.
The code your link those elements to is often the view controller ; the view controller is the object that is responsible for managing a view and the events it receives.
To make an IBOutlet, go to your interface builder file (1), select the view controller you are interested by (2, for you it will be the one it which your form is present) open the assistant editor (3), the assistant editor should show to code corresponding to your view controller. Then control drag to the inside of your view controller class definition (4).
Once you do that there will be a little "popup" asking you wether you want an outlet or an action (5), if you just want a reference to the given UI object, then select an outlet.
If the object does an action (for example a button) and you want a method to be called when this action occurs, then you would choose action.
From there this should be pretty straightforward, what I would do would be to make an outlet to the textfield containing the password, and an action for the "Send/Connect" button, whether the method linked to this event would be called, I would check if the password is right or wrong.
EDIT : Maybe I added to much details and you already know a lot about what I told you, but when beginning, those things aren't always that much easy.
I have created a subclass of a UICollectionViewCell that shows some information. I have one property in with type Weather. When an instance of that is set I want to update the cell. Is the approach below bad? I am thinking of that I may trigger the view to be created to early if I access the UI components before it is loaded. Or is that non sense and only applies to UIViewController (with regard to using view property to early)?
If this is bad, what would be the correct way?
var weather: Weather? {
didSet {
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
}
}
}
You may want an else clause, though, clearing the text field if weather was nil. Likewise, if you might update this from a background thread, you might want to dispatch that UI update back to the main thread.
Be aware that this observer is not called when you set weather in the cell's init (nor would be the #IBOutlet be configure at that point, anyway). So make sure that you're not relying upon that.
Also, if Weather is mutable, recognize that if you change the fromDate of the existing Weather object, this won't capture that. (If Weather was mutable, you'd really want to capture its changing properties via KVO, a delegate-protocol pattern, or what have you.) But if you make Weather immutable, you should be fine.
So, technically, that's the answer to the question, but this raises a few design considerations:
One generally should strive to have different types loosely coupled, namely that one type should not be too reliant on the internal behavior of another. But here we have an observer within the cell which is dependent upon the mutability of Weather.
This use of a stored property to store a model object within view is inadvisable. Cells are reused as they scroll offscreen, but you probably want a separate model that captures the relevant model objects, the controller then handles the providing of the appropriate model object to the view object (the cell) as needed.
Bottom line, it's not advisable to use a stored property for "model" information inside a "view".
You can tackle both of these considerations by writing code which makes it clear that you're only using this weather parameter solely for the purpose of updating UI controls, but not for the purposes of storing anything. So rather that a stored property, I would just use a method:
func updateWithWeather(weather: Weather?) {
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
} else {
dayLabel.text = nil
// ... more code like this
}
}
And this would probably only be called from within collectionView:cellForItemAtIndexPath:.
But, this makes it clear that you're just updating controls based upon the weather parameter, but not trying to do anything beyond that. And, coincidentally, the mutability of the weather object is now irrelevant, as it should be. And if the model changes, call reloadItemsAtIndexPaths:, which will trigger your collectionView:cellForItemAtIndexPath: to be called.
There are times where a stored property with didSet observer is a useful pattern. But this should be done only when the property is truly a property of view. For example, consider a custom view that draws some shape. You might have stored properties that specify, for example, the width and the color of the stroke to be used when drawing the path. Then, having stored properties for lineWidth and strokeColor might make sense, and then you might have a didSet that calls setNeedsDisplay() (which triggers the redrawing of the view).
So, the pattern you suggest does have practical applications, it's just that it should be limited to those situations where the property is truly a property of the view object.
I would use a property observer if I planned up updating the value during the users session. If this is a value that only gets updated when the user first loads, I would just simply call a method when my view is initially loaded.
If you use a property observer, you can give it an initial value when you define it so the data is there when the user needs it. Also, if you're updating the user interface, make sure you do it on the main queue.
var weather: Weather = data {
didSet {
dispatch_async(dispatch_get_main_queue(),{
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
}
})
}
}
Working on a shopping store app. I've been going in circles for the past week and a half trying to figure out how to correctly do this. The problem lies in a UILabel that needs to be dynamically updated with text that corresponds to checked checkboxes. It also needs to know whether to apply selection when apply button has been tapped or return previous selection if customer decided to change their mind and not tap apply. This is where I'm running into issue.
I refer to 3 controllers as image 1, image 2 and image 3.
My model is a separate class that I pass copies of back and forth to keep selections made by users when they wish to refine a collection of clothing item results.
So for example the user taps the refine button in image 1
They are taken to image 2 where they decide what they want to refine results by
They are then taken to image 3 page where they make the selection
Where problems begin:
In short in image 3 a customer makes a selection and taps done, they are then taken back to image two where their selection is shown in a string separated by commas in a UILabel underneath the chosen refine by option e.g. Gender. If they are ok with their selection they tap apply and the refining is done and displayed like in image 1. A tick is also shown in the refine button to make the customer aware refining is active.
Now lets say a selection has already been made like in the images below and the customer goes back to image 3 to modify a selection. Let's say he unchecked "microsites". What should happen is when he goes back to image 2 the list underneath the refine by option should be updated.
This works fine but I'm actually updating the property that is updated when the apply button is tapped. So if the customer decides they don't want to refine anymore and don't click apply but the back button instead to take them to image 1 then I need the original selection string to be updated in the property.
The thing is this very property is updated whenever a selection is made in image 3. So when the customer does return to image 2 then because we unchecked "microsites" only "men" will show in the string underneath the refine by option.
I've tried creating a temp property to hold the previous selection but thing's just really get messy with all this going back and forth.
Further info:
I pass my model class back and forth between controllers using the prepareForSegue method and delegation/protocols.
Image 2 is aware when the done button in image 3 is tapped. This is where I pass the model over. Image 1 knows when the apply button in image 2 is tapped and again this is where I pass the model over. To pass the model from image 1 to 2 then 2 to 3 I use the prepareForSegue method.
Image 1:
Image 2:
Image 3:
How would you do this? I feel I've made a good start by moving all my model into its own class. This has made things easier but the other problem with the UILabel is holding me back.
Update:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[[sender titleLabel] text] isEqualToString:#"Gender"]) {
VAGRefineGenderViewController *vc = [segue destinationViewController];
[vc setDelegate:self];
[vc setRefineModel:[self refineModel]];
}
}
Working code:
#implementation VAGRefineModel
{
VAGRefineModel *_object;
}
-(id)copyWithZone:(NSZone *)zone
{
VAGRefineModel *modelCopy = [[VAGRefineModel alloc] init];
modelCopy->_object = [_object copyWithZone: zone];
return modelCopy;
}
Then in image 2/controller 2 I just set the model being passed to controller 3 as the copy of the existing model.
This seems fairly simple.
You should indeed create a copy of the model and pass this to the next viewController then you don't have to worry about the edits made to it.
If the user presses back then that copy is just discarded.
If you press done then you receive the copy and replace your current model then reload your views.
It might look as simple as this (no error handling to make it easy to follow)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
{
Model *model = [self.model copy];
VC2 *viewController = segue.destinationViewController;
viewController.model = model;
viewController.delegate = self;
}
- (void)didTapDoneButtonWithModel:(Model *)model;
{
self.model = model;
[self reloadData];
}
I didn't read your post in detail, but did read enough to get the gist of it.
I'm gonna call your screen level 1 (master), level 2 (top level detail) and level 3 (fine detail) instead of image 1/2/3, because I'm talking about pictures, I'm talking about master/detail view controllers.
It sounds like you have 3 levels of view controllers that allow the user to edit finer levels of detail for a search.
I would suggest setting up your model so you can encapsulate the details handled by levels 2 and 3 into objects. When you get ready to drop to level 3, create a copy of the settings for gender and micro sites, and pass it to the level view controller. The user will interact with level 3, which has it's own copy of the settings.
Level 3 should set up level 2 as it's delegate, with a delegate message
-userChangedSettings: (Level3SettingsObject *) changedSettings
Or something similar.
If the clicks done, the level 3 VC would invoke that method on it's delegate, passing the changes to the Level3Settings object up to level 2, then pop itself/dismiss itself. (whichever is appropriate.)
If instead the user clicks cancel, just close pop/dismiss without calling the delegate method to tell the delegate about the change in settings. The settings in the level 2 object won't have changed.
The same would go for the communication between level 2 back up to level 1.
If it makes sense, you can make it so that the settings for level 1 contain level 2 data objects, and that the settings for level 2 contain level 3 data objects. That way changes to level 3 get passed back to level 2, and if the user cancels from level 2, all the changes made in level 2 and level 3 are discarded, and only if they tap save in level 2 do the changes from that level get passed up to level 1.