Adding a SubView does not display before pushViewController - objective-c

Similar to this question: Adding subview, gets delayed?
But I don't think you can pushViewController in a separate thread so is this really impossible?
Here is what I'm trying to do:
I have a TableView and when a cell is pressed, I want to call
[self.view addSubview:LoadingView]
to display an overlay with a spinner. Then I call
self.navigationController.navigationBar.hidden = NO;
[self.navigationController pushViewController:newGameViewController animated:YES];
However, the subview only displays for a split second (~1-4 seconds after the cell selection occurs while it waits for the new viewcontroller to initialize).
Is there any way to get some sort of loading indicator to occur at the instant the cell is selected?

Okay. What about this. In your didSelectRowAtIndexPath start your spinner (via addSubview ...) and start loading your stuff from the server. If that's finished remove the spinner an push your new view controller onto the stack. Make sure the user can't touch any other cell during that time. By the way. From a users perspective I'd find it mor intuitive if the new controller is loaded immediately and displays some waiting message.

Or the other way around: The newGameViewController displays the spinner and starts loading the data from the server in a background thread. When the data is complete, remove the spinner and display the data. That way the user could even go back if she doesn't want to wait.

You do not need to add code before pushing newGameViewController.
Inside viewDidLoad of newGameViewController, write the code of displaying spinner. To get the updated UI, just insert a delay before calling a web API.
inside GameViewController.m
-(void) viewDidLoad
{
[self.view addSubview:LoadingView];
[self performSelector:#selector(callWebAPI) afterDelay:0.1];
}
-(void) callWebAPI
{
//Handle network activity here..
}

Related

Show NSPopover when the application starts

I am using Pyobjc to create a NSStatusItem. When it is clicked, I am showing an NSPopOver. This is working fine. However, my requirement is to show the popover as soon as the application starts without any action by the user. Calling the callback directly in finishLaunching is not working. Is there any way to achieve this? It will be good enough even if can just simulate the click on NSStatusView.
class TestApp(NSApplication):
def finishLaunching(self):
# Make statusbar item
statusbar = NSStatusBar.systemStatusBar()
self.statusitem = statusbar.statusItemWithLength_(NSVariableStatusItemLength)
self.statusitem.setTarget_(self)
self.statusitem.setAction_('statusItemClicked:')
self.icon = NSImage.alloc().initByReferencingFile_('app-icon.png')
self.icon.setScalesWhenResized_(True)
self.icon.setSize_((20, 20))
self.statusitem.setImage_(self.icon)
self.statusitem.setHighlightMode_(1)
# self.statusItemClicked_(None)
def statusItemClicked_(self, notification):
self.viewController = SimpleXibDemoController.alloc().initWithWindowNibName_("Login")
# Show the window
self.viewController.showWindow_(self.viewController)
rect = self.statusitem.valueForKey_('button').frame()
self.viewController.popover.showRelativeToRect_ofView_preferredEdge_(rect, self.statusitem.valueForKey_('button'), NSMaxYEdge)
I finally got a somewhat sketchy solution. I have a method which positions the popover like so:
- (IBAction)showPopover:(id)sender {
[popover showRelativeToRect:self.statusItemView.bounds ofView:self.statusItemView preferredEdge:NSMinYEdge];
}
In applicationDidFinishLaunching, or finishLaunching in your case, instead of calling the method directly I called it with performSelector instead:
[self performSelector:#selector(showPopover:) withObject:self afterDelay:0];
Even setting the delay to 0 works, which I do not know why, and it now positions the popover correctly.
Edit: This seems to only work situationally. Even by creating a view controller and calling it from viewDidAppear, the popover only gets positioned half of the time.
Edit 2: I added a window controller to the status item's window, and overrode windowDidLoad. However as it turned out, the window is loaded before I can even set the controller's window so windowDidLoad is not called.
You have to wait until the view appears to present the NSPopover. Override viewDidAppear in your NSViewController subclass and present it there. (Or override loadView if your minimum deployment target is less than OS X Yosemite.)

UITableView scroll to refresh makes cells jump

I have implemented pull to refresh in a table view that is a subview to my main view like so:
UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.tableView;
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;
This updates the table and everything fine, the problem I'm having is when I touch the table to drag the cells seem to randomly shift up above the screen. This happens when I touch and drag from the middle of the screen down, or if I pull before the the cells shift back to their normal position. Here are some screen shots to better paint a picture. Keep in mind that all of these are taken after I drag down, yet the cells shift upward.
Should look like:
actually looks like these after the cells jump:
For anyone that may be having this issue or an issue like this I found a way to get the scroll to refresh to work very smoothly and not cause the issues I was having. I'm using an actual UITableViewController now instead of an embedded UITableView which helped with some of the scroll issues, but I still had an issue where instead of animating the closing of the refresh icon (table sliding up to cover the spinning circle) it just shut instantly and seemed very jarring. I put my refresh logic in the background, let it finish and then finished refreshing. My code structure is as follows:
- (void)viewDidLoad {
//set up refreshcontrol to call my refresh method
}
- (void)refresh {
[self performSelectorInBackground:#selector(getUpdatedInfo) withObject:nil];
}
Updated info runs and my TableViewController is a delegate of my class that gets refreshed information. When all the information is received and the update is completed I call the delegate method in my refreshing TableViewController class that puts the received array into the table, updates the table, and ends refreshing.
- (void)didFinishingUpdatingWithArray:(NSArray *)array {
//configure table sections
//Save the data
[self.tableview reloadData];
[self.refreshControl endRefreshing];
}

Getting this warning "while a presentation is in progress" (Xcode)

In a project I'm writing I get this error when I present a new view controller:
Attempt to present.... while a presentation is in progress!
I think it happens because I first present a new view controller, and then in that view I present another view controller.
- (void)loadLabelSettings {
LabelSettingsViewController *labelSettings =
[[LabelSettingsViewController alloc] init];
labelSettings.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:labelSettings animated:YES completion:nil];
}
The program doesn't crash or anything it runs just fine, and there is no errors or warnings in my code. So my question is: Is it something I should be concerned with and if yes how do I solve it?
Thanks in advance :)
It is, like you said, probably caused by presenting two view controllers at the same time. Wait with presenting the second view controller until the first one has been fully presented. A good location would be to do this in viewDidAppear.
In my case, I connected a UIViewControllers UIButton with a second UIViewController by a UIStoryboardSegue. Inside my code a called it a second time programmatically. So pressing the UIButton caused presenting the specified view two times.
I figured out my problem, as Scott wrote it was because I was presenting 2 view controllers at the same time. It happened because I had a button that had a UILongPressGestureRecognizer, that showed the new view controller. The problem was that when using a UILongPressGestureRecognizer, the method that is being called, is called twice. First when the long press is detected and when your finger is released from the screen. So the presentViewController method of the same view, was called twice. I fixed this by only reacting to the first detection. Here is the code :
- (void)loadButtonSettings:(UILongPressGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
}
}

Reload data from table view after come back from another view

I have a problem in my application. Any help will be greatly appreciated. Basically it is from view A to view B, and then come back from view B.
In the view A, it has dynamic data loaded in from the database, and display on the table view. In this page, it also has the edit button, not on the navigation bar. When user tabs the edit button, it goes to the view B, which shows the pick view. And user can make any changes in here. Once that is done, user tabs the back button on the navigation bar, it saves the changes into the NSUserDefaults, goes back to the view A by pop the view B.
When coming back to the view A, it should get the new data from the UIUserDefaults, and it did. I used NSLog to print out to the console and it shows the correct data. Also it should invoke the viewWillAppear: method to get the new data for the table view, but it didn't. It even did not call the tableView:numberOfRowsInSection: method. I placed a NSLog statement inside this method but didn't print out in the console.
As the result, the view A still has the old data. the only way to get the new data in the view A is to stop and start the application.
Both view A and view B are the subclass of UIViewController, with UITableViewDelegate and UITableViewDataSource.
Here is my code in the view A :
- (void)viewWillAppear:(BOOL)animated {
NSLog(#"enter in Schedule2ViewController ...");
// load in data from database, and store into NSArray object
//[self.theTableView reloadData];
[self.theTableView setNeedsDisplay];
//[self.theTableView setNeedsLayout];
}
In here, the "theTableView" is a UITableView variable. And I try all three cases of "reloadData", "setNeedsDisplay", and "setNeedsLayout", but didn't seem to work.
In the view B, here is the method corresponding to the back button on the navigation bar.
- (void)viewDidLoad {
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:#selector(savePreference)];
self.navigationItem.leftBarButtonItem = saveButton;
[saveButton release];
}
- (IBAction) savePreference {
NSLog(#"save preference.");
// save data into the NSUSerDefaults
[self.navigationController popViewControllerAnimated:YES];
}
Am I doing in the right way? Or is there anything that I missed?
When a view is first loaded, it calls the viewDidLoad method. If you create a stack, drill down into it (from A to B) and then return (B to A) the viewDidLoad does not get called again on A. What you may want to try is passing A into B (by passing in self) and call the viewDidLoad method to get the new data and then reloadData method on the the tableView to refill the table view.
What you may want to try is taking the data fetching and setting functionality out of the viewDidLoad method and place it in its own getData method. At the end of the getData method, you could place a [self.tableView reloadData]; to reset/refill the table view. From class B, you could call [self getData] and minimize the amount of work you would do in class B. This would help increase reuse-ability of that code and may prevent side effects from calling the viewDidLoad method.
You could also use viewDidAppear. It is called every time the screen appears. For performance reasons, set a flag so you don't repeat the same functionality in viewDidLoad with viewDidAppear for the first screen view.

Select row After UIPickerView is loaded

I have an iPhone database app that loads a UIPickerView with data from a table. I want to set the selected row to a particular row from the data in another table.
For example: Let's say I have a UIPickerView that is loaded with X number of names of the iPhone users' friends (the number of names is variable; it could be 1 or 1000 and all are entered into the database by the user). The iPhone user has a preference set that their current best friend is TED. I want the UIPickerView to be position to TED when displayed.
Where do I call selectRow?
I tried in viewDidAppear, but it was never called, in titleForRow which caused all kinds of strange behavior. viewDidLoad and viewWillAppear are out of the question, because I don't know what's in the datasource to the picker yet.
Call it after you retrieve the data. I don't know where you load the data so this is just a mockup of what should work. If this doesn't make sense, let me know where you are loading data so I can piece it together in my head :)
-(void)viewDidLoad
{
// load data from sql
// self.someDataArray = DATAFROMSQL
[picker reloadAllComponents];
[picker selectRow:0 inComponent:0 animated:YES];
}
There is a common issue when a (default) UIPickerView item needs to be selected when the UIPickerView is initially shown. The problem is related to the sequence of events. From reading previous posts, it seems that the [pickerView selectRow:n] is normally called in the view controllers ViewDidLoad event. However, the UiPickerView data is only loaded after the ViewDidLoad event, which means any selection code in ViewDidLoad will have no effect. To remedy, place the selection code in ViewDidAppear;
- (void) viewDidAppear:(BOOL)animated {
[m_pickerView selectRow:nSelectedItem inComponent:0 animated:YES]; }
Place this wherever you initialize the UIView that contains your UIPickerView as a subview:
[myPickerView selectRow:rowWithTedsName inComponent:columnWithNames animated:NO];
I have just found that on the 4.3 SDK's the component loading behaviors are different between the iPhone and the iPad. On the iPhone, I was about to invoke selectRow: right after initializing the view before or after adding to the subview hierarchy. On iPad however, the selectRow call yielded no results. I wound up using a performSelector:withObject:afterDelay: call to wait 0.1 seconds before firing selectRow:inComponent:animated:. This had predictable results on the iPhone and iPad.
For Swift it's simply:
self.picker?.selectRow(0, inComponent: 0, animated: true)
If you want the selected row permanently highlighted use this
myPickerView.subviews[0].subviews[0].subviews[2].backgroundColor = myPickerView.tintColor;