I'm developing IOS messanger app, I have inbox(tableview) in which I have cells(conversations) and when I select a conversation, I would like to present this conversation(tableviewController full of messages), but i dont like how much time it takes to present this controller. So my idea was to create whole controllers(tableviewController full of messages) objects before selecting conversation, and then just push them. First time I select conversation, it is blank, after going back and then selecting it again, it work. Problem is obvious, some variables are initialized in viewDidLoad method. I have tried to move them to init method but then every time conversation was blank.
Do you have any experiences with this? Any hint will be appreciated a LOT.
Thank you!!!
in tableviewController full of messages:
.h file:
#property (nonatomic, assign) BOOL firstAppear;
.m file
self.firstAppear = NO; //in init method
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
if (self.firstAppear) {
//add a indicator view here
}
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (self.firstAppear) {
//get tableView data here, then [tableView reloadData] to show data
//remove the indicator
self.firstAppear = NO;
}
}
It sounds to me like you are doing premature optimization. Creating and pushing a table view controller should take a small fraction of a second. If it's taking longer, something is wrong. Are you loading the conversation data from a remote server or something?
You might want to use Instruments to figure out what is taking extra time and causing a delay. Then you can focus on the actual cause rather than guessing.
Related
I have game that is played on a single view, when the game is over I want the user to be able to press a button (play again) that will completely reload the view (clearing all game data and refreshing the view as if it were loading for the first time). I have tried
[self.view setNeedsDisplay]
however nothing is happening. Do I have to manually clear out the data or is there a way to reset everything at once?
What I've similarly done in this case is to create a property which holds all of your game subviews, etc.
#property (nonatomic, strong) UIView *gameView;
Then in viewDidLoad, we call a method that sets up our game view for the first time (You will see we will use the same method a little later again for resetting the game)
- (void)viewDidLoad
{
[super viewDidLoad];
[self setUpGame];
}
- (void)setUpGame
{
// Your game views (subviews, buttons, etc.) set up here
self.gameView = [[UIView alloc] initWithFrame:self.view.bounds];
}
Then, when the user taps the play again button, we simply remove the game view from the superview, and call the prior method we discussed, which sets up the game again. Your button's target should be set to call this method below: [self playAgain];
- (void)playAgain
{
[self.gameView removeFromSuperview];
// This is the method we previously discussed above
[self setUpGame];
}
It's up to you to think of some cool and unique animations to make it a pleasing resetting of the game at this point :)
setNeedsDisplay just indicates that you would like iOS to redraw the screen, which you rarely should need to call manually.
I would probably implement something like #troop231 already stated, which is a reset method, but I would not re-allocate any buttons / views etc because that could be costly. In the MVC model, you should have your data (scores, # of lives, etc.) stored separately and your views should just reference them. So, reset the model and assuming you have your views aware of model changes, they will update accordingly.
Ways to do this include KVO, NSNotification, Core Data's NSFetchedResultsController (probably overkill), delegation, etc.
What I am trying to achieve is simple, from first thinking though. I found it hard to handle at last.
I would like to push a table view as a selection list, user select one cell and the cell string was sent to the previous view as selected string, simple huh??
See two pictures first
what bothers me is that:
I would like to provide (at least) two buttons, one on the left is back button auto-generated by navigation controller, and the right one is for editing. And the navigation controller is defaulted to have two buttons (from my knowledge). So there is no place for "Done" button, which is supposed for user to tap and then confirm and pop to the previous view.
So, when the user tap a cell, "Wearing" for example, I would like the following to happen, automatically and visually SEEable for user:
user can SEE that "Housing" cell is unmarked
then user can SEE that "Wearing" cell is marked
then after a little time gap (say 0.2 second), pop to the previous view and update the selection, automatically.
At first I thought it's easy but it's definitely not. Here is my code for doing it, but working wired
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0ul);
dispatch_async(queue, ^{
//unmark previous cell
if (selectedIndexPath!=nil) {
[[self.tableView cellForRowAtIndexPath:selectedIndexPath]setAccessoryType:UITableViewCellAccessoryNone];
}
selectedIndexPath=indexPath;
//get the selected cell to mark
UITableViewCell *cell=[self.tableView cellForRowAtIndexPath:indexPath];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
dispatch_sync(dispatch_get_main_queue(), ^{
//wait a little
[NSThread sleepForTimeInterval:0.2];
//return to previous view
NSLog(#"here.........");
if ([objectToUpdateCategory respondsToSelector:#selector(updateCategoryTo:withSelectedIndexPath:)]) {
NSLog(#"sending.......... update info");
[objectToUpdateCategory updateCategoryTo:cell.textLabel.text withSelectedIndexPath:selectedIndexPath];
NSLog(#"sent update info");
}
[self.navigationController popViewControllerAnimated:YES];
});
});
The tricky thing is that if I put [self.navigationController popViewControllerAnimated:YES]; to the last, the view will not visually update the unmark and mark step and go back to the previous view immediately. At first, when I didn't consider the unmark thing, the “queue" stuff in code can do the mark step visually before popping back, but sometimes not working. I don't know if my code is correct, actually I don't quite understand this queue tech from apple. But I'm pretty sure it has something to do with NSThread / queue or else that handle concurrency. I've checking Apple documents for a whole day and found no direct answer.
Hope someone could help me on this, thanks in advance :)
To "after a little time gap (say 0.2 second), pop to the previous view", use the performSelector:withObject:afterDelay: methods or one of its variants, e.g.:
[self performSelector:#selector(delayedPop) withObject:nil afterDelay:0.2];
and put the popViewControllerAnimated in the delayedPop method, e.g.:
-(void)delayedPop{
[self.navigationController popViewControllerAnimated:YES];
}
First of all, as I wrote in my comment, you shouldn't update the UI on a background thread. This will cause a lot of problems, including the UI not being updated immediately. In your case you don't need to use dispatch_async or dispatch_sync at all. What I would do is create a property in the view controller that displays the categories table view:
#property (nonatomic, weak) id<CategoryControllerDelegate> delegate;
When you push the category controller on the stack you set your expense controller as the delegate. Then, when the user makes a selection in the category controller, you call a method on the delegate (defined in a protocol), for example the one in your code sample:
#protocol CategoryControllerDelegate<NSObject>
#optional
- (void) updateCategoryTo: (NSString*) category withSelectedIndexPath: (NSIndexPath*) path;
#end
After that you pop the current view controller off the stack.
I have a UIViewController which displays a table of data that is pulled from an online database. I have a singleton manager to handle the pulling of this data and provide the data the table needs.
This is an example of how the manager works:
#property (nonatomic) NSArray *dataArray;
...
- (void)refreshDataSource
{
[AClass fetchInBackgroundWithCompletionHandler:^(NSArray *objects) {
self.dataArray = [NSArray arrayWithArray:objects];
}
}
...
- (NSArray *)tableViewDataSource
{
return self.dataArray;
}
The view controller requests an update by calling -refreshDataSource in -viewDidLoad but in the meantime provides its UITableView with cache data from the manager by pointing to -tableViewDataSource.
When the view controller presents itself for the first time, everything is fine. The second time I go to present the same view controller, the app hangs. The network request doesn't fire either.
The only fix I've found is moving my -refreshDataSource call to -viewDidAppear: instead. But it itches me why this would be happening and discomforts me that something here must be wrong.
If anyone could provide any help or suggestions that would be great!
Your question hasn't explained everything, but here are a couple of ideas that might help you.
1) viewDidLoad is only called the first time your view loads. If you switch to a different view, then return to your tableView, refreshDataSource will not be called.
2) viewDidLoad might be firing before an array has been allocated and initialised, so it's nil when you're refreshing the data, whereas viewDidAppear might not have the same problem.
I can't give a more concrete answer without more information. Can you explain "the first time, everything is fine. The second time I go..." more clearly? Step-by-step what you do, if possible.
I am trying to implement a simple spinner while my app seeks data from the Internet. My data grab is working fine, but it sometimes takes a while, so I'd like to use a UIActivityIndicatorView to let the user know that the app hasn't crashed. But it's currently not working. I've gleaned as much as I can from these forums, and others, but for some reason it's not working. Again, I feel like this solution is probably very simple. Here's what I'm doing:
I declare the spinner as a property in the .h file:
#property (strong, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
I have also dragged a spinner object onto the viewcontroller using storyboard, and created a connection to the property using CTRL-drag.
The button that triggers the URL request (and consequent loading & parsing of data) actually triggers a segue, so I figure using the
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
function is as good a place as any to get things started. I don't have a specific function attached to the button apart from the prepareforsegue function. (Maybe this is an issue? does it need to have an IBAction associated?...)
Anyway, that function triggers this:
[self GoAheadAndGetData];
which, in turn, begins like so:
- (void)GoAheadAndGetData {
[NSThread detachNewThreadSelector:#selector(threadStartAnimating:) toTarget:self withObject:nil];
....... etc
So that function looks like this:
- (void) threadStartAnimating:(id)data
{
[activityIndicator startAnimating];
}
And finally, at the end of the GoAheadAndGetData function, I have placed the stop animating call, like so:
[activityIndicator stopAnimating];
Is that clear? Please let me know if anyone needs more info - I have tried to strip it down to basics, so I don't have to post a ton of code here...
Thank you in advance - I'd really appreciate any help I could get. I don't quite know why it's not working at this point.
I am building an interface, where I can add events like in a calendar.
In the AddAEventViewController I have Buttons to set the starttime, duration and recurrence.
Every time you press a button a viewcontroller comes up with a UIDatePicker, where you can set your time. The picked component is than displayed in a UITextField. Now when I press the Done-Button, it dismisses the ModalViewController and I am back to my AddAEventViewController. Next to the Durationbutton e.g. is a UILabel, where I want to show now the just picked and in the textfield shown duration.
How do I get access to the AddEventViewController out of an other ViewController? I tried to alloc and init a new one there, but it didnt work!
- (IBAction)pressedDoneButton:(id)sender {
_mainAddWishViewController.labelDuration.text=textFieldDuration.text;
[self dismissModalViewControllerAnimated:YES];
}
Can someone help me please!
Thank you Jules
There are several ways you can do this, all of them documented here. Reading and understanding them will help you a lot in iOS software development.
There are many ways to achieve this. Here is one that is fairly straightforward.
In the "child" viewController, add a delegate property and set it to the parent view controller.
Then in your Done button handler, do something like:
[self.delegate performSelector:#selector(didComplete) withObject:self]
In the parent view controller, define a method as follows:
- (void) didComplete: (YourSubViewControllerClass *) sender
{
self.labelDuration.text = sender.textFieldDuration.text
}
Basically, this implements an informal protocol whereby the subViewController informs the main view controller that it is finished and input values are available.
Note that if you cancel out of the subViewController, don't send the didComplete message.