It's this case again:
I wish to fill the prototype cells with the names of the friends selected in the UIPickerView over there. I have programatically filled the picker with the string representation of my Player object data, and set its properties using the delegate functions.
The "New Game Friends View" you see here has its own viewcontroller subclass, as has the table view, which I attempt to embed into a UIView on the "New Game Friends View". The table view IS an instance of my WHGFriendTableViewController class. I know this because this function does not throw any exceptions:
- (IBAction)addBtnClicked:(id)sender {
WHGFriendTableViewController* tabView = (WHGFriendTableViewController*) [[self childViewControllers] objectAtIndex:0];
NSInteger row = [friendPicker selectedRowInComponent:0];
[[tabView selectedFriends] addObject:[[self friendList] objectAtIndex:row]];
[[tabView tableView] reloadData];
}
Now the problem is: while the function above does not throw any exceptions, it still does not work. It appears that nothing really happens when I insert the objectAtIndex:row into the NSMutableArray selectedFriends (which is a property) in the table view's view controller.
This:
NSLog(#"New length: %d", [[tabView selectedFriends] count]);
prints 0 after inserting the new object. I have no idea why. Printing the count of [self friendList] gives three, just as I expect. The reloadData message does not make anything appear in the table view.
Any ideas why I cannot insert new data into the table view with my code, when this seems to be working with no exceptions whatsoever? Thanks in advance!
Have you alloced & init your NSMutableArray selectedFriends?
Also have you set the dataSource and delegate of your table view?
Related
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
I have two view controllers.
First one is Custom view controller that loads the images from asset library.
Second view controller shows the full size of selected image with cancel & Delete button
I have used the below code for delete the selected image from custom view controller.
customviewcontroller.m
-(void)deleteItemsFromDataSourceAtIndexPaths:(NSArray *)itemPaths
{
//here i want to control the delete option when cancel pressed
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
for (NSIndexPath *itemPath in itemPaths) {
[indexSet addIndex:itemPath.row];
}
[self.selectedAssets removeObjectsAtIndexes:indexSet];
}
/* call the delete function*/
- (void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *selectedItemsIndexPaths = [self.collectionview1 indexPathsForSelectedItems];
[self deleteItemsFromDataSourceAtIndexPaths:selectedItemsIndexPaths];
[self.collectionview1 deleteItemsAtIndexPaths:selectedItemsIndexPaths];
}
This working fine for delete the selected image from custom view controller.
but It works also cancel. Now, I want to control deletion on cancel.
kindly help me to solve this.
I have already tried using button tag for identify which button was pressed. but can't control in custom vc
secondview.m
- (IBAction)CancelPhoto:(id)sender{
[Cancel setTag:1]; //set tag value at cancel
}
There are several ways to pass date between view controllers. I'm giving you a very simple solution as per your requirements:
Assuming you have all your images in a mutable array in first view controller.
NSMutableAray *imagesArray;
Make a property images array of NSMutableArray type in second view controller.
#property(nonatomic, strong) NSMutableAray *imagesArray;
assign your array to property while pushing/presenting view
secondController.imagesArray = imagesArray;
On delete event remove that image from array.
[self.imagesArray removeObjectAtIndex:selectedIndex];
dismiss/pop your second view where ever you want and refresh your first view controller in viewWillAppear of viewDidAppear method.
As you have passed reference of your main images array, both classes (view controllers) share same array through pointer and a change in array from either side will be reflected in both screens
There are 3 ways (in my opinion):
Delegate
Notification
In case: you are using database, just remove image url and reload data
how can I add an object in my array each time I enter to my TableView
I put this code in viewDidLoad and viewDidAppear methods but it seems to doesn't work
:
- (void)viewDidLoad
{
[super viewDidLoad];
if (!myArray) {
myArray = [[NSMutableArray alloc] init];
}
[peopleListe insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
NSLog(#"%#",myArray);
}
when I put this code in a button it works
Thank you for your help
viewDidLoad is called once, when the view loads.
viewWillAppear is called every time you go into that view.
If you want to do something each time a view appears, put the code in viewWillAppear.
EDIT: It's possible that your array is getting dealloc'd. Try setting a breakpoint in dealloc as a simple way to see if that's the case:
- (void)dealloc {
NSLog(#"BYE!); // <-- put your breakpoint here
}
If it is, you'll have to (a) store your data somewhere else, or (b) keep this view/controller from being dealloced.
Also, who is your tableViewDelegate? That will have to implement methods returning the number of items in the table view and so on. I recommend having a read through the docs to get all those relationships sorted out.
You don't want to have that array as a property/ivar of your view controller. The view controller may, and will, get deallocated when it's not used (e.g. if it's inside the navigation controller, and you tap the "back" button to go to the previous screen.) When the view controller gets deallocated, your array obviously ceases to exist.
I suggest creating keeping that array in a separate place, e.g. in a singleton data object, or even (as a quick short-term solution) your app delegate.
About the code you posted: keep in mind that [UIViewController viewDidLoad] is only called once during the view controller's lifecycle. It may get called more than once, but that would mean that the original instance has been dealloc'd (and your original array is gone).
My application is using JASidePanels libraries and I had set them using storyboard. The center view is a UITableView and the left panel is a view in which I call a method (by pressing a button)
- (IBAction)reloadAllFilters:(UIButton *)sender{
MasterViewController *masterController = [self.storyboard instantiateViewControllerWithIdentifier:#"centerViewController"];
NSMutableArray *arrayOfFilterIds = [[NSMutableArray alloc] init];
// another code
masterController.filterIdsToDisplay = arrayOfFilterIds;
[masterController.tableView reloadData];
[self.sidePanelController toggleLeftPanel:self];
}
The thing is, that when the app starts, the number of rows returned by number, but after I call this method, the number of rows is 30 but I cant see nothing.
I think that the problem is that I am instantiating a new masterController and therefor I cant get the actual table view to be displayed? Or am I wrong? Can you please help me?
Yes you are right. You create a new master controller every time you call the method
From UIStoryboard reference:
This method creates a new instance of the specified view controller each time you call it.
You need to get the existing one.
Please set the delegate and datasource to your tableview
like this ...
tableView.delegate = masterController;
tableView.dataSource = masterController;
I have a tableview with UITextFields to build a form. I then have a button in the toolbar which launches a modal view controller to select some data which is then passed back to the tableview. However, the new data that was selected does not get refreshed into the UITextField using textField.text = valueReturnedFromModal syntax. Is there something I'm missing?
I see that the data is being returned properly from the modal so that is not the issue. I'm just having trouble forcing the UITextField to refresh with the new data. I've tried forcing a reloadData on the tableview as well.
So from the modal view, here's the code that passes data back:
- (void)doneAccountSelection:(id)sender
{
[delegate didSelectAccount:currentAccount];
[self dismissModalViewControllerAnimated:YES];
}
and here's the actual method in the delegate:
- (void)didSelectAccount:(SFAccount *)selectedAccount
{
//Ensure a valid deal exists for the account to be attached to
[self createDealObjectIfNeeded];
//Set the deal account
[self.deal setAccount:selectedAccount];
//Refresh the text fields
//Tag 3: Account Name field
UITextField *acct_name = (UITextField *) [self.view viewWithTag:3];
[acct_name setText:self.deal.account.field_acct_name_value];
//Tag 4: Account City field
UITextField *acct_city = (UITextField *) [self.view viewWithTag:4];
[acct_city setText:self.deal.account.field_acct_city_value];
//Save the context changes. A new deal gets created above if one does not exist.
if ([self saveModel]) NSLog(#"Acct object created, attached to deal successfully!");
[self.tableView reloadData];
}
Would you mind posting the code that sets the text fields data and the code that passes the data back to the table view?
Answer:
It appears that you are properly setting the data, however, you are dismissing the modal view controller after sending the delegate message. In this scenario, the table view is not even created until after the dismissModalViewControllerAnimated: has ended. Which means the textfields are NULL until the view is present. What I suggest is call
[self dismissModalViewControllerAnimated:YES];
as the first line in the didSelectAccount: delegate method. This would dismiss the modal view and then continue on with your setting the data to valid textfields as -viewWillAppear: / -viewDidAppear: would have already been called. Everything seems ok it's just the order that may be tripping you up. Although
-dismissModalViewControllerAnimated:
is passed to its parent if a view controller does not have a modal view (because it is the modal view), it seems more appropriate to call this method in the delegate method where you will eventually manipulate the view due to new data, etc.
use this after getting Value in text field:
[self.tableView ReloadData];
After some serious debugging found that self.account was being used in cellForRowAtIndexPath to set the initial UITextField values within the UITableViewCell. Then after modal selection completed, I was only updating the account reference for the deal object and not updating the self.account object.
I then added to this by creating a new method called updateTextFieldsAfterModalFinished and moved the code to update the UITextFields there. This method was then called from didSelectAccount which is the delegate method for modal view that is dismissed. Things are now working as expected and the UITextFields get updated after modal selection is finished.
Just have a quickly question (more of a curiosity thing) based on a problem I just solved (I will post the answer to my problem in the post, which can be found here: My former question
The thing is that I have this UITableView which contains custom cell objects. Every time you enter this view, I generate new cells for the UITableView like this:
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"UploadCellView" owner:self options:nil];
cell = customCell;
}
Which happens in the standard method:
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Now the problem is that my custom cell objects listens for NSNotifications about upload objects happening in the background, so they can update its model data to their labels and progress bars etc. It happens like this (this is a method from the custom cell objects):
-(void) uploadProgress: (NSNotification*)notification
{
NSDictionary *userInfo = [notification userInfo];
NSNumber *uploadID = [userInfo valueForKey:#"uploadID"];
if (uploadID.integerValue == uploadActivity.uploadID)
{
UIProgressView *theProgressBar = (UIProgressView*)[self viewWithTag:progressBarTag];
[theProgressBar setProgress:(uploadActivity.percentageDone / 100) animated:YES];
UILabel *statusText = (UILabel*)[self viewWithTag:percentageTag];
[statusText setText:[NSString stringWithFormat:#"Uploader - %.f%% (%.01fMB ud af %.01fMB)", uploadActivity.percentageDone, uploadActivity.totalMBUploaded, uploadActivity.totalMBToUpload]];
}
}
When an upload finish they simply do this:
-(void) uploadFinished: (NSNotification*)notification
{
NSDictionary *userInfo = [notification userInfo];
NSNumber *uploadID = [userInfo valueForKey:#"uploadID"];
if (uploadID.integerValue == uploadActivity.uploadID)
{
[self setUploadComplete];
[[ApplicationActivities getSharedActivities] markUploadAsFinished:uploadActivity];
NSLog(#"BEGINNING RELOAD");
[parentTable reloadData];
NSLog(#"ENDING RELOAD");
}
}
Now the problem is when they call their owning tableview. When the view which the tableview is contained within dismisses, the old custom cell objects are still alive in the background getting NSNotfications. And when that upload is then done, the old custom cell objects from the former table views still tries to call that parentTable property which was set at that time, now resulting in calling random junk memory.
The way I solved this was to keep an array of all cell objects getting created in the table and then make them stop listening when the view is dismissed like this:
-(void) viewWillDisappear:(BOOL)animated
{
for (UploadCell *aCell in lol)
{
[aCell stopListening];
}
[self.navigationController popViewControllerAnimated:YES];
}
But this seems like a bit of a hack. How would I go about making sure that the custom cell objects are deleted when the view is dismissed? Because when the view is intialized again, new cells are simply made anyways, so I have no use for the old ones.
The custom view cells have a strong property pointer to the tableview they get associated with, but I thought the ARC would make sure that TableView pointer would not get invalidated then? Obviously it is somehow. Maybe because of the containing view being deleted when popped?
Sounds like the cells have a retain property pointing back to your UITableViewDataSource class.
They should instead have an assign property, then they will be released properly when the table view is released (which it currently cannot be if your cells are retaining it).
Also, the cells should shut down notifications when they are dropped out of the tableview, by overriding the cells didMoveToSuperview method:
- (void)didMoveToSuperview
{
[super didMoveToSuperview];
if ( [self superview] == nil )
{
[self unsubscribeFromYourNotifications];
}
}
That is so if they scroll off screen they will not be wasting resources updating things.
Have you considered a separate update model that keeps a map between uploadIDs and cells that listens for the notification? That way, the cells aren't responsible for updating the table themselves, the update model would do it. When the table goes away, you can shut down the update model.