Help With NSNotifcation and Asynchronous Downloading - iphone

I am sending a notification from one view to another view. My problem is that the notification in the view that I am calling in my cellForRowAtIndexPath method is only getting sent when the tableview is scrolling. How can I stop this and make it send the notification once the images have downloaded? Here is my code: https://gist.github.com/756302
Thanks
MKDev

as far as I understand your code, the message will trigger the reload of the whole table. That should lead to a refresh of the cells.
Thus, you'll need to check in line 76, if the cell is being drawn because a reload was triggered from the finish-message (and the image is now ready to display) or if you need to start the asynchronous download of the image.
The first thing which comes into my mind to check this is to set a property in reloadTableView:
- (void)reloadTableView
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"aaa"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"name" object:nil];
NSLog(#"removeobserver");
loadImageFinished = YES;
// if your table has several sections you'll need to adopt the section number
NSIndexSet *indices = [[NSIndexSet alloc] initWithIndex:0];
[self.tableView reloadSections:indices withRowAnimation:UITableViewRowAnimationFade];
[indices release];
}
and then to add in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
if (loadImageFinished) {
...
} else {
[asyncImage loadImageFromURL:[NSURL URLWithString:pathImage]];
}
...
}
Note that there could be other reasons why the table is being reloaded - the view could have been disappeared or unloaded and you might not wish to trigger your asynnchronous loading several times.

Your code should work right, when the connectionDidFinishLoading, you call the NSNotificationCenter to send the notification, there is no post method in cellForRowAtIndexPath

Related

Enable all textfields in UITableViewCell when in Edit mode

I have a bunch of custom UITableViewCells with a label and textbox. I have the textbox disabled but I want to make it so when the user taps the Edit button it will make the textboxes editable. How can I do this so that ALL the UITextFields in the UITableView become enabled?
I have
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self.navigationItem setHidesBackButton:editing animated:YES];
if (editing) {
}
}
but cannot add the textbox enable in there since I don't have access to all the textfields. Would I need to add code to grab all the cells and loop through them and enable the textfields?
I would do this by setting a isEditing BOOL on your UITableViewDelegate in the setEditing:animated: method and just updating visible cells when the value is changed.
NSArray *visibleCells = [myTable visibleCells];
for (MyTableViewCell *cell in visibleCells)
cell.textField.enabled = isEditing;
Then, using your UITableViewDelegate again, update new cells as they appear in tableView:willDisplayCell:forRowAtIndexPath:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.textField.enabled = isEditing;
}
Edit your subclass of UITableViewCell and register your instances for an editing notification in your subclass's viewDidLoad or init method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(disableTextBox) name:#"EditingIsEnabled" object:nil];
And implement a method called disableTextBox that disables the text box for that cell.
Then in your setEditing:animated method, post the notification when you want to start editing:
[[NSNotificationCenter defaultCenter] postNotificationName:#"EditingIsEnabled" object:self];
Override the method dealloc in your UITableViewCell and remove yourself as an observer, or you'll crash:
[[NSNotificationCenter defaultCenter] removeObserver:self];
If you're not using ARC, make sure to call [super dealloc]. If you're using ARC, do not call super.
You can do the same thing when you want to disable all the cells, just post a notification with a different name like EditingIsDisabled.
Let me know if you need me to flesh out the code a bit more.
Edit: I like DBD's method better in this situation.

scrollToRowAtIndexPath not scrolling to inactive/unloaded cells

I've noticed that scrollToRowAtIndexPath:atScrollPosition:animated: doesn't scroll to cell that are not currently in view, so If I have 100 cells and I need to get to the one at 70, the call to that selector will do nothing.
Is there a way I can get that cell into memory? I already have the cell's index path...
I need to scroll to that position in my app when the user would want to go there.
Thanks for any thoughts!
EDIT: #dasblinkenlight
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide) name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillHide
{
//Load remote cell here then scroll
// :( dont know how to load remote cell yet
}
- (void)keyboardWillShow
{
//Load remote cell here then scroll
// :( dont know how to load remote cell yet
//_cellIndexPath gets assigned on didSelectRowAtIndexPath:
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_cellIndexPath.row inSection:_cellIndexPath.section] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
EDIT2:
- (void)keyboardWillShow
{
//Load remote cell here then scroll
[NSThread detachNewThreadSelector:#selector(keyboardWillShowThreaded) toTarget:self withObject:nil];
}
- (void)keyboardWillShowThreaded
{
[NSThread sleepForTimeInterval:2.0];
[self performSelectorOnMainThread:#selector(keyboardWillShowMainThread) withObject:nil waitUntilDone:YES];
}
- (void)keyboardWillShowMainThread
{
//Get the cell
//_textFieldThatHasFirstResponder is a subview in the cell
//This returns null, probably because the cell is not loaded into memory
UITableViewCell *cell = [_textFieldThatHasFirstResponder superview];
NSLog(#"CELL TO SCROLL TO: %#",cell);
NSIndexPath *indexPathForCell = [self.tableView indexPathForCell:cell];
[self.tableView scrollToRowAtIndexPath:indexPathForCell atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
OK, I've got the cause of this, see, you have:
NSIndexPath *indexPathForCell = [self.tableView indexPathForCell:cell]; // nil here
[self.tableView scrollToRowAtIndexPath:indexPathForCell atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
When you send indexPathForCell: for an out-of-view cell it returns nil, so tableView doesn't know where to scroll to.
you can implement a delegate so that you can call it from the class where you are in, so that is can update the position
"I've noticed that scrollToRowAtIndexPath:atScrollPosition:animated:
doesn't scroll to cell that are not currently in view, so If I have
100 cells and I need to get to the one at 70, the call to that
selector will do nothing. "
No. it is not true. I have just tried with a tableview with 100 rows. The following code works well.
[tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:78 inSection:0]
atScrollPosition:UITableViewScrollPositionNone animated:YES];
I don't think adding sleep is changing anything. It just delays execution but does not affect the order. Can you check if the index your are passing to scrollToRowAtIndexPath is valid? I remember seeing the same problem myself but it was related to invisible cell. It was impossible to retrieve invisible cell (tableView returned nil) and therefore its index path was nil and thus scrolling failed.
You could store locations of all cells or compute it on the fly and then pass it to
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated;
It's the only solution I can imagine.

TableView not reloading from NSNotification

I'm having some trouble getting a tableView to reload using NSNotification.
My set up is that I have a filter view which allows the user to apply filters to the table view, once they click to change a filter I am sending an NSNotification which is picked up in the AppDelegate, this then reloads the core data applying the filters as a predicate.
Once the data has been loaded from core data I post another Notification to the tableView which tells it to reload it's data.
Using NSLog's I can see that the notifications are being sent and received although the [self.tableView reloadData] is doing nothing.
Here is my function to reload the data:
-(void)reloadTable:(NSNotification *)notification {
NSLog(#"reloading table");
[self.rootTableView reloadData];
}
Calling reload in the same way from inside the controller while it is currently in view works fine, so I'm pretty stuck as to why this isn't working.
Is the notification arriving on a thread other than the main thread? It would be if the notification were sent from a background thread. If this is the case, you'll need to marshall the reloadData call to the main thread. Something like this:
[tableView performSelectorOnMainThread: #"reloadData" withObject:nil waitUntilDone:NO];
This is for catching notification. You can put this in viewDidLoad.
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserverForName:kSFAccountsChanged object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[tblView ReloadData];
}];
}
The code below will fire a notification:
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center postNotificationName:kSFAccountsChanged object:self];
1)
Have you attached your table to rootTableView in interface builder?
Try adding
NSLog(#"%#", self.rootTableView);
This should not be null :)
2)
If you have attached correctly, can you post the code in your table view delegate methods to see what's going on in there?

Question on edit button on sqlite

I am trying to show minus symbol when I show data from an sqlite database, but if we show an edit button it shows a delete symbol. I am using navigation controller to show data. I want to show when the page loads, it must be with a "-" symbol without edit button? Does anyone have an example or tutorial?
If I understand you correctly, you want to delete the row immediately, without confirmation of tapping a delete button.
First: Users are used to having a delete button confirmation, and if you delete immediately, they may be surprised.
Second: If the issue is deleting many rows quickly, could you instead allow the user to select multiple rows and delete them all at once (like in the email app)
Third: A solution to your question:
Make a custom UITableViewCell and override this function
- (void)willTransitionToState:(UITableViewCellStateMask)state {
if (state && UITableViewCellStateShowingDeleteConfirmationMask) {
[delegate deleteCell:self];
}
}
You will need to also have a delegate property on the UITableViewCell subclass and set it to your view controller whenever you create a new cell. Then add this function in your view controller:
- (void) deleteCell:(UITableViewCell *)cell {
NSIndexPath *idx = [self.table indexPathForCell:cell];
if (idx) {
NSArray *arr = [NSArray arrayWithObject:idx];
//Delete from database here
if (/*delete success*/) {
[self.tableView deleteRowsAtIndexPaths:arr withRowAnimation:UITableViewRowAnimationFade];
}
}
}
Still not sure exactly what you're looking for, but if I understand better this time, you just want the - symbol to show up as soon as the view appears?
- (void) viewDidLoad {
[super viewDidLoad];
[self.tableView setEditing:YES];
}

What's with [UITableView reloadData]?

I have an application that has a UITableView. This UITableView is populated by an NSMutableArray being held (as a property) in the appDelegate. You can think of this as an email window. It lists messages in a subclassed UITableViewCell. When a new message appears, I have all the code done which downloads the message, adds the data to the appDelegate's NSMutableArray which holds all of the messages. This code is working fine.
Now, once the new message is downloaded and added to the array, I am trying to update my UITableView using the following code, however - the UITableView's delegate functions do not get called.
The odd thing is when I scroll my UITableView up and down, the delegate methods finally get called and my section headers DO change (they show the message count for that section). Shoudn't they update in real-time and not wait for my scrolling to trigger the refresh? Also, the new cell is never added in the section!!
Please Help!!
APPDELEGATE CODE:
[self refreshMessagesDisplay]; //This is a call placed in the msg download method
-(void)refreshMessagesDisplay{
[self performSelectorOnMainThread:#selector(performMessageDisplay) withObject:nil waitUntilDone:NO];
}
-(void)performMessageDisplay{
[myMessagesView refresh];
}
UITableViewController Code:
-(void) refresh{
iPhone_PNPAppDelegate *mainDelegate = (iPhone_PNPAppDelegate *)[[UIApplication sharedApplication] delegate];
//self.messages is copied from appDelegate to get (old and) new messages.
self.messages=mainDelegate.messages;
//Some array manipulation takes place here.
[theTable reloadData];
[theTable setNeedsLayout]; //added out of desperation
[theTable setNeedsDisplay]; //added out of desperation
}
As a sanity check, have you verified that theTable is not nil at that point?
You could try putting a delay on the reloadData call - I had a similar problem when I was trying to get my tableview to update when reordering cells, except that the app crashed if I called reloadData during it.
So something like this might be worth a try:
Refresh method:
- (void)refreshDisplay:(UITableView *)tableView {
[tableView reloadData];
}
and then call it with (say) a 0.5 second delay:
[self performSelector:(#selector(refreshDisplay:)) withObject:(tableView) afterDelay:0.5];
Hope it works...
If you call reloadData from within a dispatched method, make sure to execute it on the main queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^(void) {
// hard work/updating here
// when finished ...
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.myTableView reloadData];
});
});
..same in method form:
-(void)updateDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^(void) {
// hard work/updating here
// when finished ...
[self reloadTable];
});
}
-(void)reloadTable {
dispatch_async(dispatch_get_main_queue(), ^(void) {
[myTableView reloadData];
});
}
Have you tried setting a breakpoint in your refresh method just to be sure your messages array has the correct content before calling reloadData?