EDIT: I have Found the answer to my own question. Please look at the bottom of my post.
I am having an animation issue trying to push a UIViewController in didSelectRowAtIndexPath on a UITableView programmatically. When I run this code in iOS 6 it works fine. In iOS 7 the animation is messed up (it animates to the left about 20% of the screen then disappears). If I do this in storyboard the animation is fine. Am I missing something or is there a way around this without using storyboard?
// This is a cut down example.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController *viewController = [[UIViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
}
This is what it looks like right before the tableView disappears. I understand why it does this. Its because the new view being pushed should animate sliding in over it as it does when using storyboard. For some reason it does not work programmatically.
EDIT: This is all I missed
For some reason you have to set a backgroundColor.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIViewController *viewController = [[UIViewController alloc] init];
[viewController.view setBackgroundColor:[UIColor orangeColor]];
[self.navigationController pushViewController:viewController animated:YES];
}
This is the default "parallax" behavior triggered by the pushViewController:animated: method in iOS7.
Having little experience with storyboards I suspect that their segues are different from UINavigationController push/pop animations.
You can override the default push/pop by building custom animated or custom interactive transitions.
Or you could use the UIViewController method:
transitionFromViewController:toViewController:duration:options:animations:completion:
to customize this behavior. Hope this helps and hope I've understood your question...
I've been have similar problems which I managed to fix by delaying the call
[self performSelector:#selector(displayMyViewController) withObject:nil afterDelay:0.2f];
I have experienced this issue too. Another reason for this would be modifying the destination view's alpha. At least that's what i did.
If you want to change the view's opacity when you're displaying a loading HUD or something, don't modify the view's alpha.
Instead, adjust the alpha of the Table View or Collection view, or whatever you have in that view. If you have several elements, put them all in a view and change that view's alpha.
Another reason would also be the background color, as stated above. You need to set the background color of the destination view. I ran into this several times as well.
Related
EDIT: Please watch the video of my issue here:
https://www.dropbox.com/sh/lzgs9mahx5mea13/AADLYfLQix7MDleDN1ER81qVa?dl=0
I have had an app live in app store which works perfectly fine on iOS 9.
However on iOS 10 (tested on device iPhone 6s with latest beta), when the cell on the master view controller is selected and the detail view is "pushed", my navigation bar's title and navigation bar buttons disappear.
Only the back button is visible.
Even if I pop back to the master by clicking back button or swiping back, they don't come back. After popping back, even the "master's" title and bar buttons are gone. I have no clue how to troubleshoot this as there are no errors.
IN my code, I am not hiding the navigation bar anywhere nor doing anything fancy with the navigation controller.
Screenshots from view hierarchy insprector:
Notice how the title and my right bar buttons on behind a few other views. the back button is at the very front. This shows that the buttons and title are not hidden, they are being covered by 3 extra views: UIVisualEffectView, _UIVisualEffectBackdropView and _UIVIsualEffectFilterView
Also in the video, you will notice that if i do a half swipe back, then cancel the swipe, the bar buttons come back. But the title doesn't.
After returning to the master, notice the master's nav bar stuff is overlaid with 2 other private class views:
I push to detail programmatically:
Relevant code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
PlaylistDetailViewController *pdvc = (PlaylistDetailViewController*)[self.storyboard instantiateViewControllerWithIdentifier:#"PlaylistDetailViewController"];
pdvc.indexPath=indexPath;
[self.navigationController pushViewController:pdvc animated:YES];
}
I ran into this problem too, and all the solutions suggested so far are either:
too complicated
doesn't work
In the end I found out that this was caused because of the updated draw cycle for UINavigationBar in iOS10.
To get around this I had to fix it with:
self.navigationController.navigationBarHidden = YES;
self.navigationController.navigationBarHidden = NO;
It's basically triggering the navigationbar to redraw.
It's still annoying how they can just push out a new version of OS that breaks something this significant.
I ran into this same issue but it was caused from using a custom UINavigationBar that was adding a blur view. It looks like something has changed with iOS10 that when adding a title or buttons to the navigation bar they are being added at a specific index instead of being appended to the subview stack.
I was able to overcome this issue by overriding the method insertSubview:atIndex and making sure the blurView was always inserted at the back of the subview stack.
I was getting the same issue like u facing now. There are some changes i did in my code and its working. In my viewWillAppear write a code of navigation in dispatch_async
dispatch_async(dispatch_get_main_queue(), ^{
//BACK BUTTON CALLING
//NAVIGATION TITLE
});
[super viewWillAppear:animated];
This will help you to set your title and back button with the help of main queue.
actually, i just figured out a minute ago. I am using a custom GKFadeNavigationController from github.com/gklka/GKFadeNavigationController AFter removing it, that fixes the issue.
Same problem applies if you are using the LTNavigationBar library (https://github.com/ltebean/LTNavigationBar)
The workaround for me was to change the code in UINavigationBar+Awesome.m:
Replace
[[self.subviews firstObject] insertSubview:self.overlay atIndex:0];
with
[[self.subviews firstObject] insertSubview:self.overlay atIndex:self.subviews.count -1];
Swift 3.0 workaround for this:
Subclass UINavigationBar and override insertSubview(_ view: UIView, at index: Int)
override func insertSubview(_ view: UIView, at index: Int) {
if let _ = view as? UIVisualEffectView {
super.insertSubview(view, at: 0)
} else {
super.insertSubview(view, at: self.subviews.count - 1)
}
}
I found a solituion for my work. Create a view (viewBackground) with all the images and colors that conform the navigation bar and then y convert it in a image and use it like a background.
UIGraphicsBeginImageContextWithOptions(viewBackground.bounds.size, viewBackground.opaque, 0.0);
[viewBackground.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[UINavigationBar appearance] setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
I am creating a UITableViewController (at the root of a UINavigationController) and presenting this modally on top of another view controller. I have it working to an extent, such that when the view loads and viewDidAppear is called, the visual effect looks good. But right after viewDidAppear, the visual effect goes away and the tableview has a white background.
In my presented UITableViewController, I added this in viewDidLoad:
if (NSClassFromString(#"UIVisualEffectView") && !UIAccessibilityIsReduceTransparencyEnabled()) {
self.tableView.backgroundColor = [UIColor clearColor];
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
[blurEffectView setFrame:self.tableView.frame];
self.blurView = blurEffectView;
self.tableView.backgroundView = self.blurView;
}
I also implement this, to make sure the cells have a clear background:
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if (NSClassFromString(#"UIVisualEffectView") ) {
cell.backgroundColor = [UIColor clearColor];
}
}
I also set the cell.backgroundColor = [UIColor clearColor] in tableView's cellForRowAtIndexPath: but that doesn't help either.
Again, I get the correct effect when the view loads, but it loses the blur background as soon as it appears on screen. Any ideas what might be going wrong? I have also tried to retain the blueEffectView as a property in my view controller, but no luck
Are you using Storyboards?
Select the view controller that's used modally (or if it's wrapped in another VC, select the wrapper) and set the Presentation combo box to "Over Full Screen" or "Over Current Context". (I don't actually know what the difference is, they both seem to do the same thing.)
After doing that, your blur effect should Just Workâ˘. Also, I think this is new in iOS 8, I haven't tested this in iOS 7 yet.
The problem is, when you present a view controller modally with the default settings, once it has finished taking over the screen, the underlying view controller seems to be essentially erased. This is why your blur effect looks great until the presentation animation completes - the underlying view vanishes.
To implement the desired behavior, you'll need to change the segue presentation style to something that will preserve the underlying view controller, like Over Full Screen instead of the default Full Screen.
Note that when you dismiss this modal presentation, viewDidAppear will not be called on the presenting view controller, because it's now always visible. That can bite you if you were relying on that method to perform any function upon dismissal.
I've got a UIViewController that I would like to display over top of my UITabBarController kind of like a custom UIAlertView. The problem is that, when I present the UIViewController, I'm unable to resize its view to fit whatever screen is currently being used. The UIViewController (ShareViewController is its name) is designed using a .xib file, and it's currently using iPhone 5 screen height. I basically just want to resize its UIView (view) if the app is being run on any device, but it won't work when I call setFrame.
Here is some code:
ShareViewController * svc = [[ShareViewController alloc] initWithNibName:#"ShareViewController" bundle:nil];
self.tabBarController.modalPresentationStyle = UIModalPresentationCurrentContext;
[self.tabBarController presentViewController:svc animated:NO completion:nil];
[svc.view setFrame:[[UIScreen mainScreen] applicationFrame]];
I use the "UIModalPresentationCurrentContext" setting so I can show the ShareViewController with a transparent background. So, when I run this code the ShareViewController just gets added but goes off screen because it is still at the iPhone 5 display height.
What can I do to resize my ShareViewController's main view?
Instead of presentviewcontroller better make it as addsubview and apply animation like present modal view controller
I gave the group of buttons a container which I then translated upwards depending on the screen center. This is a bit of a hack fix but I think there is a better way to implement this custom UIViewController anyway.
I have a UIViewController called DashBoardViewController that acts as delegate for a UITabBar. In its xib I have placed a UITabBar with 3 UITabBarItem.
Each of these items activate a different View Controller, let's call them ViewController1, ViewController2, ViewController3
DashBoardViewController is supposed to show ViewController1 and select the first bar on loading, so in my initWithNibName I have what follows:
...
ViewController1* vc = [[ViewController1 alloc] initWithNibName:#"ViewController1" bundle:nil];
[self.view addSubview:vc.view];
self.currentViewController = vc;
...
I implement the UITabBarDelegate having something as follows:
if (item == viewController1Item) {
ViewController2 *vc2 = [self.childrenControllers objectAtIndex:1];
[self.currentViewController.view removeFromSuperview];
[self.view addSubview:vc2.view];
self.currentViewController = vc2;
} ...
Problem
The View Controller in the first UITabBarItem always works as expected, extending it to the full size of thew view.
However, in the second and following tabs, this doesn't happen: the view doesn't extends. This shows if, for example, I align a tab with the bottom in the ViewController2 XIB: this will not be at the bottom when viewed inside the UITabBarItem.
Note
Please note that this is not related to the XIB: if I invert ViewController1 and ViewController2, it will be ViewController1 the one failing to extend. It's related to the UITabBarItem.
Ideas
Possibly, this depends by the way I addSubview when I call the DashBoardViewController's initWithNibName. But I can't find a way to explain this.
Other details
All the XIB are set with "Size = none".
I can't really speak to the way you have your XIB setup without seeing it, but I can make a couple of suggestions.
The behaviour that you're trying to implement by removing & adding subviews to DashBoardViewController should really be handled by a UITabBarController. This provides a UITabBar, a view for your content and handles the logic of switching between UIViewControllers while keeping layout sane and being part of the SDK.
If for some reason you can't, or don't want to use a UITabBarController, I'd suggest implementing a viewWillLayoutSubviews method on your DashBoardViewController, like so:
- (void)viewWillLayoutSubviews
{
if( self.currentViewController )
{
self.currentViewController.view.frame = self.view.bounds;
}
}
Maybe also try adding the self.currentViewController.view.frame = self.view.bounds; line after you've swapped ViewControllers too, for good measure. This will make sure that the frame of your current ViewController's view is always sized to fill the bounds of DashBoardViewController's view.
This isn't the 'Proper' way to do it though, I'd really recommend using a UITabBarController if you can, since you don't know how much else of UITabBarController you'll end up re-implementing if you start rolling your own controller.
Any further problems will most probably be to do with the internal layout of your sub-ViewControllers, rather than their size / position in DashBoardViewController's view.
On your XIB File make sure that your set the flexible height to stick to top and bottom, this way the UITableView will always have the same height as the 4" display
I'd like to place an ADBannerView object onto my UITableView screen statically, what means that I want it to always stay above my toolbar (self.navigationController.toolbar), even when the user is scrolling the tableview. I've solved this by adding by ADBannerView as a subview to my toolbar and given it negative values for the frames origin:
[self setBannerViewSize];
[self.navigationController.toolbar addSubview:bannerView];
The only problem is: I can't click and open the iAd this way - I can see the banner but nothing happens when I tap on it.
Since I'm also using a refreshControl, the option to use a UIViewController instead of UITableViewController and add a tableView manually wouldn't work for me. Is there any other way I can get my ADBannerView statically showing in my table view controller AND still being tappable?
Thank you in advice!
Yay!! After all I succeeded in solving this (really annoying) problem by myself (and a lot of reading around)!
First, I found this really world-changing post. Basically this post handles with the topic that a UITableViewController uses self.view for its tableView property, so overriding the tableView property (or synthesizing it manually) plus giving self.view a new view (from application) and adding tableView as its subview would make it possible to reach the real superview of tableView.
But this still didn't solve my problem, although I was sure it would, because it all made sense. My bannerView appeared in the right place (and was fixed) but it still didn't do anything when clicked. But there was a second minor thing I didn't know about:
As I read in this post the superview of a subview doesn't only have to be userInteractionEnabled but also have a non-transparent backgroundColor. Because my superviews background color was set to [UIColor clearColor] it all didn't work - but setting its backGroundColor to e.g. blackColor solved the whole problem: the bannerView got finally tappable! :)
So, my code is now looking like this:
#synthesize tableView;
- (void)viewDidLoad
{
[super viewDidLoad];
if (!tableView && [self.view isKindOfClass:[UITableView class]]) {
tableView = (UITableView *)self.view;
}
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
self.tableView.frame = self.view.bounds;
[self.view addSubview:self.tableView];
[self resizeTableToFitBanner];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:bannerView];
// some other code
}
BannerViewController in Apple's iAdSuite sample code solves this problem very elegantly:
https://developer.apple.com/library/ios/samplecode/iAdSuite/Introduction/Intro.html
I think you should use a container view, and set things up in IB. You can add a tool bar and ADBannerView to the bottom of the view of your navigation controller's root view controller. Fill the rest of the space with a container view - this will give you an embedded view controller automatically. You should delete this one and then drag in a tableViewController and control drag from the container view to the tableViewController to hook up the embed segue.