Animate UIView above all other views - ios

I want to display/animate a UIView above the whole content of my app. The following approach works, but is not a very good idea to attach everything to the window.
[[[[UIApplication sharedApplication] delegate] window] addSubview:view]
What is a good alternative?

I think you can try visibleViewController method to get the top viewController
//Try this to get the top viewController
UIViewController *vc = [app.rootViewController visibleViewController];
// After getting the top viewController, and then add your subview to it
[vc addSubView:view];
// and try bringing this view to the top
[vc bringSubViewTo....(fogot~):view];
see if it could help :)

I think the approach is correct, you do need to attach it to the window, as you may not know what the view hierarchy is.
What you can do to tidy up the code a little bit, is encapsulate that logic into some methods accessible through the AppDelegate or something:
- (void)addAlwaysVisibleView:(UIView *)topmostView;
- (void)removeAlwaysVisibleView;
The AppDelegate can then keep the state, and know if an "always visible" view is already attach, to keep things in order and do some housecleaning when needed.

Related

Pushing a new VC into the navigationController seems to have no effect

This problem sounds quite basic but I don’t understand what I am overlooking.
I am trying to push a new view controller into a navigation controller, however the topViewController remains unaffected.
#import "TNPViewController.h"
#interface TNCViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
#implementation TNCViewController
-(void)userDidSelectNewsNotification:(NSNotification*)note
{
TNPViewController *nextViewController = [[TNPViewController alloc] init];
[[self navigationController] pushViewController:nextViewController animated:YES];
UIViewController *test = [[self navigationController] topViewController];
}
The test shows an instance of TNCViewController instead of TNPViewController. How is this possible?
UPDATE
Thanks for everyone's participation. The method name indicating notifications is a red herring. I found the problem, as Stuart had mentioned previously but deleted later on. (As I have high reputation score, I still can see his deleted post).
My initial unit test was this:
-(void)testSelectingNewsPushesNewViewController
{
[viewController userDidSelectNewsNotification:nil];
UIViewController *currentTopVC = navController.topViewController;
XCTAssertFalse([currentTopVC isEqual:viewController], #"New viewcontroller should be pushed onto the stack.");
XCTAssertTrue([currentTopVC isKindOfClass:[TNPViewController class]], #"New vc should be a TNPViewController");
}
And it failed. Then I set a breakpoint and tried the test instance above and it still was showing the wrong topviewcontroller.
At least the unit test works if I change
[[self navigationController] pushViewController:nextViewController animated:YES];
to
[[self navigationController] pushViewController:nextViewController animated:NO];
A better solution is to use an ANIMATED constant for unit tests to disable the animations.
This doesn't really answer your question about why your navigationController is not pushing your VC. But it is a suggestion about another possible approach.
You could instead add a new VC on the Storyboard and simply activate the segue when the userDidSelectNewsNotification method is activated. Then change the information accordingly to the event in the VC, specially since you are initializing it every time anyway.
This is something of a stab in the dark, but the issue is hard to diagnose without more information.
I see you're trying to push the new view controller in response to a notification. Are you sure this notification is being handled on the main thread? UI methods such as pushing new view controllers will fail (or at least behave unpredictably) when not performed on the main thread. This may also go some way to explaining the odd behaviour of topViewController returning an unexpected view controller instance.*
Ideally, you should guarantee these notifications are posted on the main thread, so they will be received on that same thread. If you cannot guarantee this (for example if you're not responsible for posting the notifications elsewhere in your code), then you should dispatch any UI-related code to the main thread:
- (void)userDidSelectNewsNotification:(NSNotification *)note
{
dispatch_async(dispatch_get_main_queue(), ^{
TNPViewController *nextViewController = [[TNPViewController alloc] initWithNibName:#"TNPViewController" bundle:nil];
[self.navigationController pushViewController:nextViewController animated:YES];
});
}
Also, it appears you are not initialising TNPViewController using the designated initialiser (unless in your subclass you are overriding init and calling through to initWithNibName:bundle: from there?). I wouldn't expect this to cause the transition to fail entirely, but may result in your view controller not being properly initialised.
In general, you might be better creating your view controllers in a storyboard and using segues to perform your navigation transitions, as #Joze suggests in his answer. You can still initiate these storyboard segues in code (e.g. in response to your notification) with performSegueWithIdentifier:, but again, be sure to do so on the main thread. See Using View Controllers in Your App for more details on this approach.
*I originally wrote an answer trying to explain the unexpected topViewController value as being a result of deferred animated transitions. While it is true that animated transitions are deferred, this does not prevent topViewController from being set to the new view controller immediately.

iOS - compare current top view with another view

I was looking for a way to find the current top view presented (including Modal Views) and on Stackoverflow some said this was the code to do so:
UIView *topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
But I then want to compare that topView to a certain view controller I am in (let's say FirstViewController), to know if the currently presented View Controller is this particular VC.
How can I compare a ViewController to this topView? There are so many different answers on the internet about which code to use and I don't find any that gives me a good solution.
Basically, let's say I am in FirstViewController.m, I only want to present an alert if the current top view is FirstViewController.m (it works through NSNotification, so right now is also presents an alert from FirstViewController, even when I'm on another ViewController).
What is the right code to use?
Thank you
I found an acceptable answer finally:
if(weakSelf.isViewLoaded && weakSelf.view.window){
//view is visible
}
This works perfectly. Just one important thing: DON'T USE IT IN viewDidLoad. The view.window will be nil in viewDidLoad, because it won't be added to the application window yet.

UIViewController incorectly shows on its side

I have two views, when that app loads if the prefs match the users log in I am trying to load the next ViewController immediatly.
Each view normally shows in landscape and I have set the apps settings to only allow landscape. However when I try to load the second view immediatly from the first view "viewdidload" The second view appears on its side.
This is what my code looks like, this code is in a method that gets called from the first viewcontrollers viewdidload
currentProjectListViewController = [[CurrentProjectListViewController alloc] initWithNibName:#"CurrentProjectListViewController" bundle:nil];
[currentProjectListViewController setDelegate:self]; // set the delegate so it can access the returning method to update the view
UIWindow* keyWindow= [[UIApplication sharedApplication] keyWindow];
[keyWindow addSubview: currentProjectListViewController.view];
[self presentViewController:currentProjectListViewController animated:NO completion:nil];
I have no idea where to even start with this. it makes it to the second views viewdidload how ever like I say the view appears to be correct but its just on its side effectivly 90degrees incorrect.
any help making sure this apperar in landscape would be greatly appreciated.
I think your View hierarchy is somewhat wrong.
Why are you adding view of currentProjectListViewController to UIWindow object and immediately after adding it as subview you are presenting it using presentViewController from the current ViewController?
When you load and show the second ViewController, your presentViewController code should work.
You dont need to play with your UIWIndow object. Just make sure you dismiss your present view controller so as to reduce memory footprint and memory leaks.

What can be the all possible scenarios that make uiview jump up 20px

...
[[UIApplication sharedApplication] setStatusBarHidden:YES];
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[window makeKeyAndVisible];
...
This is the code inside my app delegate which is loading a viewcontroller (or root view controller). This viewcontroller performs some checks and load another view according to checks.
But the views loaded by viewcontroller is jumping 20px up, i searched a lot regarding this, mostly status bar is blamed, so I tried to hide it, but no use.
Please clear me one more thing whats the difference between initWithNibName and init seems like both doing same job.
After searching a lot and still not getting any solution thats why i want to know, What can be the all possible scenarios that make uiview jump up 20px
...manually adjusting the view size is not a good practice in this case. I want to hunt down the actual cause of this jumping so the problem can be fixed in a standard way not with hacks.
Difference in initWithNibName and init for a viewController is that initWithNibName you can specify any name for this viewController nib file, where init only searches for specific nib names to load
For example,
If your viewController name is MyViewController, then when you call init, the nib controller with name MyViewController.xib is searched and loaded
Where if you call initWithNibName, you can specify any nib name
about the jumping issue, i am too guessing that is a status bar issue, please present more information

Two ViewControllers Orientation

I've come up with a strange scenario, I'm using one NavigationViewController and one ViewController (named- container) in my MainWindow.xib.
NavigationController loads subsequent views, and in parallel, the other ViewController (container) loads some images on top of everything - No matter what view is displayed by the NavigationViewController.
When I rotate the device, the subsequent views of NavigationController rotates as expected but the container and its subsequent views do not rotate.
Here is the screenshot of my MainWindow.xib
and here is the code.
in .h
UINavigationController *navigationController;
IBOutlet UIViewController *container;
in .m (ApplicationDidFinishLaunchingWithOptions)
[window addSubview:navigationController.view];
[window addSubview:container.view];
[window makeKeyAndVisible];
I've also tried creating a separate class and assigned it to the viewController. (Its ViewDidLoad) Method fires but it doesn't come in the (ShouldRotateToInterfaceOrientation)
I read somewhere that IOS doesn't support the orientation for multiple ViewControllers.
I think you should try this because you are trying to use tow views in one navigation.So dont make sub view of window,make subview of main View like:-
[window addSubview:navigationController.view];
[navigationController.view addSubview:container.view];
[window makeKeyAndVisible];
I have't try this code but i am just suggesting idea.
thanks.
There should only be one viewController to a screen, unless you are using container views.
See the Implementing a Container View Controller section of the class reference.
The very short version is that you need to create your own container and then add the two view controllers to it using the methods that they describe:
Here are the essential methods you might need to call:
addChildViewController:
removeFromParentViewController
transitionFromViewController:toViewController:duration:options:animations:completion:
willMoveToParentViewController:
didMoveToParentViewController:

Resources