I have a scenario where I needed to add an animation inside the Navigation Bar, but the simulator doesn't animate. I add a UIView, add a label/text field and create outlets for the constraints and update them with "UIView animateWithDuration:" Instead, the simulator just updates the constraints and displays the view accordingly, but without any animation. I have tried it by modifying both constraints as well as frames (separately).
[self.view layoutIfNeeded];
[UIView animateWithDuration: 3
delay: 0.0
options: UIViewAnimationOptionCurveEaseInOut
animations: ^{
self.constraint.constant += 60;
NSLog(#"inside animation");
[self.view layoutIfNeeded];
}
completion:nil];
This is shown when I increment the width constraint by "60" in viewDidLoad: . And it comes out non-animated.
When I configure the constraints (increment width by "60") through a button click, even a static updation doesn't happen.
The same code works perfectly outside the navigation bar inside the UIView, though.
Appreciate any help...
If your navigation bar contains some animation, then you could try out drawing custom navigation bar. Here's what you've to do :
In your view controller in viewDidLoad() method add the following code
self.navigationController.navigationBarHidden = YES;
After that using a UIView create a custom navigation bar with frame adjusted to be visible and top.
After that add components on the view and start working with animations.
You can achieve that by taking custom UIView and create width NSLayoutConstraint outlet to it and in code you can handle the constant property of this variable by calling UpdateConstraintsIfNeeded or UpdateViewConstraints method before calling animation code
Ok, I've solved this at last. Phew...
What I did was...
Added a view in place of barButtonItem (let's call this "superView") and over it another view (and let's call this "subView") so that I get the following configuration.
Constraints were set between subView and objects on top of it. Now, I created outlets (superView and subView) for superView and subView, made them transparent (optional, of course).
Useful note: Set the superView's width to the default bar button item's width and the subView's to the screen width. Disable "Clip Subviews" on superView but enable on the subView.
Added animations as needed where I had wanted, but layoutIfNeeded on the "superView", not the parent view.
[self.superView layoutIfNeeded];
Good luck.
Related
I have a simple UIScrollView
UIScrollView *mainScrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
[self.view addSubview:mainScrollView];
[mainScrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
When the view is loaded and shown, part of UIScrollView appears under the navigation bar. But after rotation the view is suddenly considers navigation bar and pins the view to its bottom.
How can I make a view consider navigation bar from the beginning?
To start UIScrollView at bottom of UINavigationBar then i would suggest you to set Adjust Scroll View Insets property and setting Extend Edges property as well. do the needful changes of below property as per your requirement.
Ok, I solved this issue.
Problem has been solved by moving creation of scrollView to viewDidLoad method. Somehow, if scrollView is created outside the "load" methods, we can face such kind of problems.
I created an UIScrollView inside a UIView and have put a couple labels and buttons in it.
The buttons and labels go on longer than the view frame, so I put this in the viewControllers viewDidLoad, like so:
-(void)viewDidLoad {
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
self.scrollView.contentSize = CGSizeMake(321, 600);
self.scrollView.frame = self.view.frame;
}
I also set Bounce Vertically to true, so I can bounce the view lower, and it seems as if it's 'scrolling' but then as soon as I let go and it 'unbounces', it goes away.
Why won't my scroll view scroll and stay down?
How are you adding the labels and buttons? If you are using Storyboard and Auto Layout, you will need to set constraints.
Check my answer here on how to do it.
First of all, you should try adding this line:
[self.scrollView setScrollEnabled: YES];
I am trying to add iAd into my project. The view controller that i am trying to iAd into contains a UIWebView. A UIToolBar is also added at the bottom. Above the tool bar, i have dragged a ADBannerView inside my StoryBoard. This is how it looks like:
To show the ads, this is what i have done so far: Added iAd frameWork, created an IBOutLet from AdBannerView named "banner", in viewDidLoad i assigned the delegate to self. Then i added the AdBannerViewDelegate methods and also added the following method:
- (void) viewDidLayoutSubviews {
if (self.banner.bannerLoaded) {
CGRect contentFrame = self.view.bounds;
CGRect bannerFrame = self.banner.frame;
contentFrame.size.height -= self.banner.frame.size.height;
bannerFrame.origin.y = contentFrame.size.height;
self.banner.frame = bannerFrame;
}
}
Well the iAd is properly showing with all the above i have done.
PROBLEM: The first time the view is loaded, it shows the ads properly but when i unload and reload the view again, UIToolBar disappears and is covered by the AdBannerView which is shifted below in place of UIToolBar. Can anyone points out where the problem could be and how to solve it? Thanks.
Your problem is due to setting frames in the viewDidLayoutSubviews method. When using auto layout, you shouldn't set any frames. In the storyboard, when you add the addBannerView, give it a height constraint, and spacing constraints to the to sides of the view, and a vertical spacing constraint to the tool bar (the web view should also have a vertical spacing constraint to the top of the add banner view). Delete the viewDidLayoutSubviews method, and it should work properly.
Sounds like a z-index problem to me, and if this is the case, the solution is quite simple. If you're using Interface Builder, you can use the Document Outline on the left side of your screen to drag and drop views to adjust their z-index. The bottom of the list is the highest z-index and therefore the top on screen view.
Or, if you want to make the adjustments in code, instead of using addSubview:, you can use one of the following.
[<#(UIView *)#> insertSubview:<#(UIView *)#> aboveSubview:<#(UIView *)#>];
[<#(UIView *)#> insertSubview:<#(UIView *)#> atIndex:<#(NSInteger)#>];
[<#(UIView *)#> insertSubview:<#(UIView *)#> belowSubview:<#(UIView *)#>];
I implemented a custom UIViewController Transition in my App, which replaces the navigation controllers built in push animation.
Everything works so far, except the toplayoutguide in the newly pushed view controller is 0 although the new view controller inherited the navigation bar from the old view controller.
It should be 64.0 (Statusbar height + Navigation bar height), where it is 0.0 now.
So all objects, which are attached to the top layout guide in the storyboard now appear 64 points too high (below the translucent bar).
When I disable the custom View Transition the top layout guide will have the expected value.
I tried to call layoutSubviews and updateConstraints "all over the place". In the view controller as well as in the navigationcontroller.
As I understand the navigationcontroller (parentviewcontroller) should update the toplayoutguide of the new view controller, but apparently I am missing something in my custom transitioning code, which triggers the update to the correct value for the toplayoutguide.
Here's my custom transition code which is an object set as delegate of the navigationcontroller:
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.7;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIView *animationContainerView = [transitionContext containerView];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = [toVC view];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIView *fromView = [fromVC view];
CGRect endFrame = [transitionContext finalFrameForViewController:toVC];
CGRect startFrame;
startFrame = CGRectOffset(endFrame, 0, endFrame.size.height);
if (self.operation == UINavigationControllerOperationPop) {
[animationContainerView insertSubview:toView belowSubview:fromView];
[toView setFrame:endFrame];
}
else{
[toView setFrame:startFrame];
[animationContainerView insertSubview:toView aboveSubview:fromView];
}
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 usingSpringWithDamping:0.7 initialSpringVelocity:0.8 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
if (self.operation == UINavigationControllerOperationPop) {
[fromView setFrame:startFrame];
[fromView layoutIfNeeded];
}
else{
[toView setFrame:endFrame];
[toView layoutIfNeeded];
}
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
Nothing really fancy happens there. Just the view sliding from the bottom up with some built in dynamics.
The problem is, that the objects attached to the top layout guide now are under the navigation bar, as the top layout guide length == 0.
I can't figure out what I need to do, so that the view controller's toplayoutguide is set to the correct value.
The push navigation is performed "plain vanilla" with a push storyboard segue. All I do, before calling performSegueWithIdentifier is to set the navigationcontrollers delegate.
Here's the code:
self.navigationController.delegate = [[My_CustomNavigationTransitionDelegate alloc] init];
[self performSegueWithIdentifier:#"infosSegue" sender:nil];
What do I miss?
I was having an issue where the bottomLayoutGuide property would set itself to zero length and then would cause my buttons above the tab bar to fall below to tab bar with the autolayout.
Have you looked at doing this
[self.navigationController.view setNeedsLayout]
I put it into my viewwillappear and I stopped getting a zero length on the bottomLayoutGuide property. Maybe that would help you out with your topLayoutGuide property too.
I was able to work around this, with the following view hierarchy:
UIView
UIScrollView
<content, constrained to UIScrollView>
Constrain the UIScrollView to match the UIView's top, leading, trailing, and bottom edges. Interface Builder might want you to use the topLayoutGuide and bottomLayoutGuide for the UIScrollView, or it might not. Maybe it's dependent on the version of Xcode, but some of our View Controllers used the superview, others used the layout guides.
For the views where Interface Builder didn't want to constraint the scroll view relative to its superview, I opened the storyboard in a text editor and adjusted the constraints on the scroll view by hand.
Finally, on the View Controller, make sure that extend edges under top bar is YES, and so is Adjust Scroll View Insets.
Basically, I'm avoiding using the topLayoutGuide, and instead relying on the scroll view insets, which does work.
Where I didn't have a UIScrollView in the hierarchy, like you, NOT extending edges under the top bar worked for me.
I ran into the exact same problem. My custom navigation controller's container view didn't have constraints. The minute I added vertical spacing constraints from the container view to its superview's layout guides (albeit the two were identical in size), and set the top/bottom/status bar appearance on the container view everything was ok and the layout guides of the pushed controllers were in the correct position. Hope that helps.
Update: From the official documentation on topLayoutGuide
A view controller within a container view controller does not set this property's value. Instead, the container view controller constrains the value to indicate:
The bottom of the navigation bar, if a navigation bar is visible
The bottom of the status bar, if only a status bar is visible
The top edge of the view controller’s view, if neither a status bar nor navigation bar is visible
So the container view needs to implement correct constraints and hide/show bars and such for the effects to work. AFAIK there is no API to do this in custom container view controllers.
I found a way. First uncheck "Extend Edges" property of controller after that navigation bar will get in dark color. Add a view to controller and set top and bottom LayoutConstraint -100. Then make view's clipsubview property no (for navigaionbar transculent effect). My english is bad sorry for that. :)
I get a weird interface bug with my UIScrollView and I cant figure out how to solve it. I only wrote one line of code (shown below) and it is a blank project's setup easily reproducible!
Setting:
I have a UIScrollView that contains a UISegmentedControl (since the segments of
the control are loaded dynamically, it could exceed the width of the screen and the scrollView is supposed to scroll the segmentedControl horizontally, the height of the scrollview is the same as the UISegmentedControl's).
The ViewController that contains this is embedded in a tabBar (or navigation bar, which also shows the bug). The whole thing is using Auto-Layout.
Bug:
When I scroll the SegmentedControl some degree to the right and then switch the viewController by clicking the other tab on the tabBarController, the content-offset of the segmented control gets weirdly shifted when switching back to the initial viewcontroller. When I try to scroll to the leftmost part of the scrollview it won't let me. When switching the tabs a couple of times, it gets fixed again and I can do this over.
What I did (can you reproduce this?):
Create a blank single-view ios project
Embed the already given viewController in a tabbarcontroller.
Put a scrollView on the upper portion of the view that fits the screen from left to right.
Put a UISegmentedControl on the topleft corner of the scrollview and drag the scrollview to fit the segmented controls height height
Change the Segmented control's width a bit so xcode adds a width-constraint. in the segmented control's width constraint change the width constraint's relation to "greater than or equal"
create an outlet to the segmented control
in viewDidload add this code
[self.segmentedControl insertSegmentWithTitle:#"A really long title so it you have to scroll to see it" atIndex: 0 animated: NO];
Create a blank viewcontroller and add it as a second viewController for the tabbarController.
This is how my storyboard looks like:
Now run the project, scroll the segmented control to it's right end as far as it goes. Switch the tab and switch back and please tell me how your scrollview now behaves - and WHY.
My guess would be it has something to do with Auto Layout maybe? Can't figure out what though.
I tried fixing this by setting the scrollView's contentSize in viewDidAppear or changing the content offset of the scrollView in viewDidAppear or changing frames, combination of those and what not....
Extra question:
Is it no longer neccessary to set the scrollViews contentSize property? Why does it scroll the content automatically?
After googeling I found the answer in another StackOverflow question.
What you need to do is save the scrollview.contentOffset on viewWillDisappear,
set it to CGPointZero on viewDidDisappear and set it back to the saved state on viewDidLayoutSubviews:
-(void) viewWillDisappear: (BOOL) animated {
self.lastContentOffset = self.scrollView.contentOffset;
[super viewWillDisappear: animated];
}
-(void) viewDidDisappear:(BOOL)animated {
[super viewDidDisappear: animated];
self.scrollView.contentOffset = CGPointZero;
}
- (void)viewDidLayoutSubviews {
[super viewDidlayoutSubviews];
self.scrollView.contentOffset = self.lastContentOffset;
}