UIApplication.shared.keyWindow?.addSubview not working - ios13

UIApplication.shared.keyWindow?.addSubview not working in ios13
i try many options but not find any solutions.
i want every screen status bar make blue color form AppDelegate .
i try this code.
if #available(iOS 13.0, *) {
/* let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
// Reference - https://stackoverflow.com/a/57899013/7316675
let statusBar = UIView(frame: window?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero)
statusBar.backgroundColor = publicMethod().hexStringToUIColor(hex: "#033d6f")
window?.addSubview(statusBar)
*/
let statusBar = UIView()
statusBar.frame = UIApplication.shared.statusBarFrame
statusBar.backgroundColor = .blue
UIApplication.shared.statusBarStyle = .lightContent
UIApplication.shared.keyWindow?.addSubview(statusBar)
} else {
let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
if statusBar.responds(to:#selector(setter: UIView.backgroundColor)) {
//statusBar.backgroundColor = UIColor.darkGray
statusBar.backgroundColor = publicMethod().hexStringToUIColor(hex: "#033d6f")
}
}
here is screenshot for this issue.

You have probably already solved this question by now, but just in case someone else stumbles upon it in the future, I'll answer it with the code I got working.
First of, since you want to run this piece of code from the AppDelegate, I'm assuming you are running it from the didFinishLaunchingWithOptions.
In that case make sure that you don't run the code until after you have called makeKeyAndVisible() or else the keyWindow will be nil.
However, when you call addSubview() from didFinishLaunchingWithOptions the viewControllers will be added on top of it, and as such it might not be visible (at least that was the case for me, while using a navigationController).
To test this, when running the app click on the Debug View Hierarchy button in xCode as shown in the picture.
You can drag the screen to see the view hierarchy from different angles like in this picture:
If that is the case, you can make the statusBar the topmost view by setting the zPosition: statusBar.layer.zPosition = .greatestFiniteMagnitude
So my full code for this is:
func setStatusBar() {
if #available(iOS 13.0, *) {
let statusBar = UIView(frame: UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero)
statusBar.backgroundColor = .blue
UIApplication.shared.keyWindow?.addSubview(statusBar)
statusBar.layer.zPosition = .greatestFiniteMagnitude
} else {
let statusbar = UIApplication.shared.value(forKey: "statusBar") as? UIView
statusbar?.backgroundColor = .blue
}
}
Again, this will not work in iOS 13 if this function is called before makeKeyAndVisible()

Related

Removing background image from label in tornadofx

I have two css classes on a tornadofx label bound to a SimpleBooleanProperty. One which has a background image and a blue border and one which has no background image and a yellow border.
Snippet from View containing label:
val switch: SimpleBooleanProperty = SimpleBooleanProperty(false)
label("my label"){
toggleClass(UIAppStyle.style1, switch.not())
toggleClass(UIAppStyle.style2, switch)
}
Snippet from UIAppStyle:
s(style1){
textFill = Color.YELLOW
maxWidth = infinity
maxHeight = infinity
alignment = Pos.CENTER
backgroundImage += this::class.java.classLoader.getResource("img.png")!!.toURI()
backgroundPosition += BackgroundPosition.CENTER
backgroundRepeat += Pair(BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT)
borderColor += box(Color.BLUE)
}
s(style2){
textFill = Color.YELLOW
maxWidth = infinity
maxHeight = infinity
alignment = Pos.CENTER
borderColor += box(Color.YELLOW)
}
When switch = false, there is a background image and a blue border. When switch = true, there is the same background image and a yellow border. I'm not finding out how to get the background image to remove. Interestingly enough, if I add a different background image to style2, it changes correctly.
Edit: To remove two toggleClasses and introduce new strange problem:
class MyView : View(){
...
init{
...
row{
repeat(myviewmodel.numSwitches){
val switch = myviewmodel.switches[it]
val notSwitch = switch.not()
label("my label"){
addClass(UIAppStyle.style2)
toggleClass(UIAppStyle.style1, notSwitch)
}
}
}
}
This code snippet does not work for me. However, if I add private var throwsArray = mutableListOf<ObservableValue<Boolean>>() as a field of MyView and add notSwitch to the array, then the same exact code works. It's almost as if notSwitch is going out of scope and becoming invalidated unless I add it to a local array in the class?
I don’t understand why you want to have two different toggleClass for the same control. As you pointed out, the problem in your case is that when the backgroundImage is set, you need to set a new one in order to change it. But in your case, you only have to add the style without backgroundImage first and them set toggleClass with the style with backgroundImage. Like this:
label("my label"){
addClass(UIAppStyle.style2)
toggleClass(UIAppStyle.style1, switch)
}
button {
action {
switch.value = !switch.value;
}
}
Edit: This ilustrate what I'm talking about in comments:
class Example : View("Example") {
override val root = vbox {
val switch = SimpleBooleanProperty(false)
val notSwitch = switch.not()
label("my label"){
addClass(UIAppStyle.style2)
toggleClass(UIAppStyle.style1, notSwitch)
}
button("One") {
action {
switch.value = !switch.value;
}
}
button("Two") {
action {
notSwitch.get()
}
}
}
}
You can put the notSwitch.get() in any action and without trigger that action it does the work. Check how I put it in the action of button Two, but without clicking that button even once, it works.
This is actually some kind of hack, in order to achieve what you want. But I don’t see the reason why my initial solution with true as default value for property shouldn’t work.
Edited to do inverse of status
Here is simple example of a working toggle class using your styling:
class TestView : View() {
override val root = vbox {
val status = SimpleBooleanProperty(false)
label("This is a label") {
addClass(UIAppStyle.base_cell)
val notStatus = SimpleBooleanProperty(!status.value)
status.onChange { notStatus.value = !it } // More consistent than a not() binding for some reason
toggleClass(UIAppStyle.smiling_cell, notStatus)
}
button("Toggle").action { status.value = !status.value }
}
init {
importStylesheet<UIAppStyle>()
}
}
As you can see, the base class is added as the default, while styling with the image is in the toggle class (no not() binding). Like mentioned in other comments, the toggleClass is picky, additive in nature, and quiet in failure so it can sometimes be confusing.
FYI I got to this only by going through your github code and I can say with confidence that the not() binding is what screwed you in regards to the toggleClass behaviour. Everything else causing an error is related to other problems with the code. Feel free to ask in the comments or post another question.

UIBarButtonItem not change image automatically when switching dark mode/light mode

I am trying to integrate dark mode support to my app. But when setting image icon for UIBarButtonItem, it seem only works when the first time UIBarButtonItem is shown, it is not changed when I switch between dark mode/light mode.
When using that image with other UIButton, it works fine.
So I wonder if I am missing something?
P/s: I have to use that trick for updating image:
let item: UIBarButtonItem = UIBarButtonItem()
let button: UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: 26, height: 19))
button.setImage(UIImage(named: "hamburger"), for: .normal)
button.addTarget(self, action: action, for: .touchUpInside)
item.customView = button
Seems to be a bug in iOS, but one way around this is, to use traitCollection delegate method to set the correct image manually.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
let imageAsset = UIImage(named: "YOUR_IMAGE_NAME")?.imageAsset
let resolvedImage = imageAsset?.image(with: traitCollection)
self.YOUR_BUTTON.image = resolvedImage
}

Editable NSTextFields with Variable Height in NSTableView Rows in macOS App

Xcode 10.1, Swift 4.2, macOS 10.14.2
I am trying to make a simple to do list app for macOS where there are a series of NSTableView rows and inside each one is an NSTextField. Each field is a to-do item. I want the NSTableView rows to expand to fit the size of the text within each NSTextField.
I have all of the following working:
Setting the text in the NSTextField makes the NSTableView row expand as needed. Auto layout constraints are set in my storyboard.
Using tableView.reloadData(forRowIndexes: ..., columnIndexes: ...) sets the text and resizes the table row correctly.
But doing tableView.reloadData() always resets every NSTextField to a single line of text as shown here:
Interestingly, if you click into the NSTextField after reloading the whole table, the field resizes to fit its content again:
I believe I have set all the appropriate auto layout constraints on my NSTextField and I'm using a custom subclass for it as well (from a helpful answer here):
class FancyField: NSTextField{
override var intrinsicContentSize: NSSize {
// Guard the cell exists and wraps
guard let cell = self.cell, cell.wraps else {return super.intrinsicContentSize}
// Use intrinsic width to jibe with autolayout
let width = super.intrinsicContentSize.width
// Set the frame height to a reasonable number
self.frame.size.height = 150.0
// Calcuate height
let height = cell.cellSize(forBounds: self.frame).height
return NSMakeSize(width, height)
}
override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
super.invalidateIntrinsicContentSize()
}
}
⭐️Here is a sample project: https://d.pr/f/90CTEh
I'm at a loss as to what else I can try. Any ideas?
I think there is some basic problems with the constraints in interface builder. Resizing the window makes everything wonky. Also you should call validateEditing() in the textDidChange(forBounds:) in your FancyField class.
I created a sample project, that does what you want on Github
Write a comment if you have any problems with it.
Thinking a little about it, thought i would add the meat of the code here. Only thing that really needs to work, is the update on the NSTextField when the "Tasks" is being updated. Following is the code required for the NSTextField.
public class DynamicTextField: NSTextField {
public override var intrinsicContentSize: NSSize {
if cell!.wraps {
let fictionalBounds = NSRect(x: bounds.minX, y: bounds.minY, width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
return cell!.cellSize(forBounds: fictionalBounds)
} else {
return super.intrinsicContentSize
}
}
public override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
if cell!.wraps {
validatingEditing()
invalidateIntrinsicContentSize()
}
}
}
Hope it helps.

Navigation controller top layout guide not honored with custom transition

Short version:
I am having a problem with auto layout top layout guide when used in conjunction with custom transition and UINavigationController in iOS7. Specifically, the constraint between the top layout guide and the text view is not being honored. Has anyone encountered this issue?
Long version:
I have a scene which has unambiguously define constraints (i.e. top, bottom, left and right) that renders a view like so:
But when I use this with a custom transition on the navigation controller, the top constraint to the top layout guide seems off and it renders is as follows, as if the top layout guide was at the top of the screen, rather than at the bottom of the navigation controller:
It would appear that the "top layout guide" with the navigation controller is getting confused when employing the custom transition. The rest of the constraints are being applied correctly. And if I rotate the device and rotate it again, everything is suddenly rendered correctly, so it does not appear to be not a matter that the constraints are not defined properly. Likewise, when I turn off my custom transition, the views render correctly.
Having said that, _autolayoutTrace is reporting that the UILayoutGuide objects suffer from AMBIGUOUS LAYOUT, when I run:
(lldb) po [[UIWindow keyWindow] _autolayoutTrace]
But those layout guides are always reported as ambiguous whenever I look at them even though I've ensured that there are no missing constraints (I've done the customary selecting of view controller and choosing "Add missing constraints for view controller" or selecting all of the controls and doing the same for them).
In terms of how precisely I'm doing the transition, I've specified an object that conforms to UIViewControllerAnimatedTransitioning in the animationControllerForOperation method:
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController*)fromVC
toViewController:(UIViewController*)toVC
{
if (operation == UINavigationControllerOperationPush)
return [[PushAnimator alloc] init];
return nil;
}
And
#implementation PushAnimator
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.5;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] addSubview:toViewController.view];
CGFloat width = fromViewController.view.frame.size.width;
toViewController.view.transform = CGAffineTransformMakeTranslation(width, 0);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.transform = CGAffineTransformMakeTranslation(-width / 2.0, 0);
toViewController.view.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
fromViewController.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
#end
I've also done a rendition of the above, setting the frame of the view rather than the transform, with the same result.
I've also tried manually make sure that the constraints are re-applied by calling layoutIfNeeded. I've also tried setNeedsUpdateConstraints, setNeedsLayout, etc.
Bottom line, has anyone successfully married custom transition of navigation controller with constraints that use top layout guide?
Managed to fix my issue by adding this line:
toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
To:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext fromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC fromView:(UIView *)fromView toView:(UIView *)toView {
// Add the toView to the container
UIView* containerView = [transitionContext containerView];
[containerView addSubview:toView];
[containerView sendSubviewToBack:toView];
// animate
toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration animations:^{
fromView.alpha = 0.0;
} completion:^(BOOL finished) {
if ([transitionContext transitionWasCancelled]) {
fromView.alpha = 1.0;
} else {
// reset from- view to its original state
[fromView removeFromSuperview];
fromView.alpha = 1.0;
}
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
From Apple's Documentation for [finalFrameForViewController] : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewControllerContextTransitioning_protocol/#//apple_ref/occ/intfm/UIViewControllerContextTransitioning/finalFrameForViewController:
I solved this by fixing the height constraint of the topLayoutGuide. Adjusting edgesForExtendedLayout wasn't an option for me, as I needed the destination view to underlap the navigation bar, but also to be able to layout subviews using topLayoutGuide.
Directly inspecting the constraints in play shows that iOS adds a height constraint to the topLayoutGuide with value equal to the height of the navigation bar of the navigation controller. Except, in iOS 7, using a custom animation transition leaves the constraint with a height of 0. They fixed this in iOS 8.
This is the solution I came up with to correct the constraint (it's in Swift but the equivalent should work in Obj-C). I've tested that it works on iOS 7 and 8.
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let fromView = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!.view
let destinationVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
destinationVC.view.frame = transitionContext.finalFrameForViewController(destinationVC)
let container = transitionContext.containerView()
container.addSubview(destinationVC.view)
// Custom transitions break topLayoutGuide in iOS 7, fix its constraint
if let navController = destinationVC.navigationController {
for constraint in destinationVC.view.constraints() as [NSLayoutConstraint] {
if constraint.firstItem === destinationVC.topLayoutGuide
&& constraint.firstAttribute == .Height
&& constraint.secondItem == nil
&& constraint.constant == 0 {
constraint.constant = navController.navigationBar.frame.height
}
}
}
// Perform your transition animation here ...
}
I struggled with the exact same problem. Putting this in the viewDidLoad of my toViewController really helped me out:
self.edgesForExtendedLayout = UIRectEdgeNone;
This did not solve all my issues and I'm still looking for a better approach, but this certainly made it a bit easier.
Just put the following code toviewDidLoad
self.extendedLayoutIncludesOpaqueBars = YES;
FYI, I ended up employing a variation of Alex's answer, programmatically changing the top layout guide's height constraint constant in the animateTransition method. I'm only posting this to share the Objective-C rendition (and eliminate the constant == 0 test).
CGFloat navigationBarHeight = toViewController.navigationController.navigationBar.frame.size.height;
for (NSLayoutConstraint *constraint in toViewController.view.constraints) {
if (constraint.firstItem == toViewController.topLayoutGuide
&& constraint.firstAttribute == NSLayoutAttributeHeight
&& constraint.secondItem == nil
&& constraint.constant < navigationBarHeight) {
constraint.constant += navigationBarHeight;
}
}
Thanks, Alex.
As #Rob mentioned, topLayoutGuide is not reliable when using custom transitions in UINavigationController. I worked around this by using my own layout guide. You can see the code in action in this demo project. Highlights:
A category for custom layout guides:
#implementation UIViewController (hp_layoutGuideFix)
- (BOOL)hp_usesTopLayoutGuideInConstraints
{
return NO;
}
- (id<UILayoutSupport>)hp_topLayoutGuide
{
id<UILayoutSupport> object = objc_getAssociatedObject(self, #selector(hp_topLayoutGuide));
return object ? : self.topLayoutGuide;
}
- (void)setHp_topLayoutGuide:(id<UILayoutSupport>)hp_topLayoutGuide
{
HPLayoutSupport *object = objc_getAssociatedObject(self, #selector(hp_topLayoutGuide));
if (object != nil && self.hp_usesTopLayoutGuideInConstraints)
{
[object removeFromSuperview];
}
HPLayoutSupport *layoutGuide = [[HPLayoutSupport alloc] initWithLength:hp_topLayoutGuide.length];
if (self.hp_usesTopLayoutGuideInConstraints)
{
[self.view addSubview:layoutGuide];
}
objc_setAssociatedObject(self, #selector(hp_topLayoutGuide), layoutGuide, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
HPLayoutSupport is the class that will act as a layout guide. It has to be a UIView subclass to avoid crashes (I wonder why this isn't part of the UILayoutSupport interface).
#implementation HPLayoutSupport {
CGFloat _length;
}
- (id)initWithLength:(CGFloat)length
{
self = [super init];
if (self)
{
self.translatesAutoresizingMaskIntoConstraints = NO;
self.userInteractionEnabled = NO;
_length = length;
}
return self;
}
- (CGSize)intrinsicContentSize
{
return CGSizeMake(1, _length);
}
- (CGFloat)length
{
return _length;
}
#end
The UINavigationControllerDelegate is the one responsible for "fixing" the layout guide before the transition:
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
toVC.hp_topLayoutGuide = fromVC.hp_topLayoutGuide;
id <UIViewControllerAnimatedTransitioning> animator;
// Initialise animator
return animator;
}
Finally, the UIViewController uses hp_topLayoutGuide instead of topLayoutGuide in the constraints, and indicates this by overriding hp_usesTopLayoutGuideInConstraints:
- (void)updateViewConstraints
{
[super updateViewConstraints];
id<UILayoutSupport> topLayoutGuide = self.hp_topLayoutGuide;
// Example constraint
NSDictionary *views = NSDictionaryOfVariableBindings(_imageView, _dateLabel, topLayoutGuide);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topLayoutGuide][_imageView(240)]-8-[_dateLabel]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:views];
[self.view addConstraints:constraints];
}
- (BOOL)hp_usesTopLayoutGuideInConstraints
{
return YES;
}
Hope it helps.
i found way. First uncheck "Extend Edges" property of controller. after that navigation bar getting 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 bad sory for that. :)
I had the same problem, ended up implementing my own topLayout guide view and making constraints to it rather then to topLayoutGuide. Not ideal. Only posting it here in case someone is stuck and looking for quick hacky solution http://www.github.com/stringcode86/SCTopLayoutGuide
Here's the simple solution I'm using that's working great for me: during the setup phase of - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext, manually set your "from" and "to" viewController.view.frame.origin.y = navigationController.navigationBar.frame.size.height. It'll make your auto layout views position themselves vertically as you expect.
Minus the pseudo-code (e.g. you probably have your own way of determining if a device is running iOS7), this is what my method looks like:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
CGAffineTransform destinationTransform;
UIViewController *targetVC;
CGFloat adjustmentForIOS7AutoLayoutBug = 0.0f;
// We're doing a view controller POP
if(self.isViewControllerPop)
{
targetVC = fromViewController;
[container insertSubview:toViewController.view belowSubview:fromViewController.view];
// Only need this auto layout hack in iOS7; it's fixed in iOS8
if(_device_is_running_iOS7_)
{
adjustmentForIOS7AutoLayoutBug = toViewController.navigationController.navigationBar.frame.size.height;
[toViewController.view setFrameOriginY:adjustmentForIOS7AutoLayoutBug];
}
destinationTransform = CGAffineTransformMakeTranslation(fromViewController.view.bounds.size.width,adjustmentForIOS7AutoLayoutBug);
}
// We're doing a view controller PUSH
else
{
targetVC = toViewController;
[container addSubview:toViewController.view];
// Only need this auto layout hack in iOS7; it's fixed in iOS8
if(_device_is_running_iOS7_)
{
adjustmentForIOS7AutoLayoutBug = toViewController.navigationController.navigationBar.frame.size.height;
}
toViewController.view.transform = CGAffineTransformMakeTranslation(toViewController.view.bounds.size.width,adjustmentForIOS7AutoLayoutBug);
destinationTransform = CGAffineTransformMakeTranslation(0.0f,adjustmentForIOS7AutoLayoutBug);
}
[UIView animateWithDuration:_animation_duration_
delay:_animation_delay_if_you_need_one_
options:([transitionContext isInteractive] ? UIViewAnimationOptionCurveLinear : UIViewAnimationOptionCurveEaseOut)
animations:^(void)
{
targetVC.view.transform = destinationTransform;
}
completion:^(BOOL finished)
{
[transitionContext completeTransition:([transitionContext transitionWasCancelled] ? NO : YES)];
}];
}
A couple of bonus things about this example:
For view controller pushes, this custom transition slides the pushed toViewController.view on top of the unmoving fromViewController.view. For pops, fromViewController.view slides off to the right and reveals an unmoving toViewController.view under it. All in all, it's just a subtle twist on the stock iOS7+ view controller transition.
The [UIView animateWithDuration:...] completion block shows the correct way to handle completed & cancelled custom transitions. This tiny tidbit was a classic head-slap moment; hope it helps somebody else out there.
Lastly, I'd like to point out that as far as I can tell, this is an iOS7-only issue that has been fixed in iOS8: my custom view controller transition that is broken in iOS7 works just fine in iOS8 without modification. That being said, you should verify that this is what you're seeing too, and if so, only run the fix on devices running iOS7.x. As you can see in the code example above, the y-adjustment value is 0.0f unless the device is running iOS7.x.
I ran into this same issue but without using a UINavigationController and just positioning a view off of the topLayoutGuide. The layout would be correct when first displayed, a transition would take place to another view, and then upon exiting and returning to the first view, the layout would be broken as that topLayoutGuide would no longer be there.
I solved this problem by capturing the safe area insets prior to the transition and then reimplementing them, not by adjusting my constraints, but by setting them on the viewController's additionalSafeAreaInsets.
I found this solution to work well as I don't have to adjust any of my layout code and search through constraints and I can just reimplementing the space that was there previously. This could be more difficult if you are actually using the additionalSafeAreaInsets property.
Example
I added a variable to my transitionManager to capture the safe insets that exist when the transitionManager is created.
class MyTransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
private var presenting = true
private var container:UIView?
private var safeInsets:UIEdgeInsets?
...
Then during the entering transition I save those insets.
let toView = viewControllers.to.view
let fromView = viewControllers.from.view
if #available(iOS 11.0, *) {
safeInsets = toView.safeAreaInsets
}
In the case of the iPhone X this looks something like UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
Now when exiting, the insets on that same view we transitioned from in the entrance will be .zero so we add our captured insets to the additionalSafeAreaInsets on the viewController, which will set them on our view for us as well as update the layout. Once our animation is done, we reset the additionalSafeAreaInsets back to .zero.
if #available(iOS 11.0, *) {
if safeInsets != nil {
viewControllers.to.additionalSafeAreaInsets = safeInsets!
}
}
...then in the animation completion block
if #available(iOS 11.0, *) {
if self.safeInsets != nil {
viewControllers.to.additionalSafeAreaInsets = .zero
}
}
transitionContext.completeTransition(true)
try :
self.edgesforextendedlayout=UIRectEdgeNone
Or just set navigationbar opaque and set background image or backgroundcolor to navigationbar
In storyboard add another vertical constraint to main view's top. I have the same problem too but adding that constraint help me to avoid manual constraints. See screenshot here link
Other solution is to calculate toVC frame... something like this:
float y = toVC.navigationController.navigationBar.frame.origin.y + toVC.navigationController.navigationBar.frame.size.height;
toVC.view.frame = CGRectMake(0, y, toVC.view.frame.size.width, toVC.view.frame.size.height - y);
Let me know if you have found a better solution. I have been struggling with this issue as well and I came up with previous ideas.

How is this navigation control created

The second image on this page from Apple's user interface design guide show a segmented control inside of a tall navigation bar:
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/mobilehig/Anatomy.html#//apple_ref/doc/uid/TP40006556-CH24-SW1
How has this been done? It seems to me that a UINavigationBar is always 64 pixels high, so I don't understand how they made this taller.
Is it a custom element (which would be surprising in this document), or is there an easy way to achieve this? I'm wondering if it's a UIToolbar... are they merged with the UINavigationBar under iOS 7? If so, how do we do this?
Note that I need to do this in a iPad app, where the UINavigationController is inside a split view controller.
I finally found the solution to this.
I had to override UINavigation bar with my custom subclass in order to change the height. By using the appearance proxy the title and navigation items can be repositioned correctly. Unfortunately the proxy can't be used to shift the back button's arrow up (on iOS 7), so we have to override layoutSubview to handle that.
#define kAppNavBarHeight 66.0
#implementation TATallNavigationBar
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setupAppearance];
}
return self;
}
- (id)init
{
self = [super init];
if (self) {
[self setupAppearance];
}
return self;
}
- (void)setupAppearance {
static BOOL appearanceInitialised = NO;
if (!appearanceInitialised) {
// Update the appearance of this bar to shift the icons back up to their normal position
CGFloat offset = 44 - kAppNavBarHeight;
[[TATallNavigationBar appearance] setTitleVerticalPositionAdjustment:offset forBarMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearanceWhenContainedIn:[RRSNavigationBar class], nil] setBackgroundVerticalPositionAdjustment:offset forBarMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearanceWhenContainedIn:[RRSNavigationBar class], nil] setBackButtonBackgroundVerticalPositionAdjustment:offset forBarMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearanceWhenContainedIn:[RRSNavigationBar class], nil] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, offset) forBarMetrics:UIBarMetricsDefault];
appearanceInitialised = YES;
}
}
- (CGSize)sizeThatFits:(CGSize)size {
return CGSizeMake(self.superview.frame.size.width, kNavBarheight);
}
- (void)layoutSubviews {
static CGFloat yPosForArrow = -1;
[super layoutSubviews];
// There's no official way to reposition the back button's arrow under iOS 7. It doesn't shift with the title.
// We have to reposition it here instead.
for (UIView *view in self.subviews) {
// The arrow is a class of type _UINavigationBarBackIndicatorView. We're not calling any private methods, so I think
// this is fine for the AppStore...
if ([NSStringFromClass([view class]) isEqualToString:#"_UINavigationBarBackIndicatorView"]) {
CGRect frame = view.frame;
if (yPosForArrow < 0) {
// On the first layout we work out what the actual position should be by applying our offset to the default position.
yPosForArrow = frame.origin.y + (44 - kAppNavBarHeight);
}
// Update the frame.
frame.origin.y = yPosForArrow;
view.frame = frame;
}
}
}
#end
Note that it's easy to specify your subclass in XCode: clicking on the UINavigationController gives you access to the UINavigationBar in the left hand column. Click that and change it's subclass in the inspector.
I've also created a Gist for this:
https://gist.github.com/timothyarmes/7080170

Resources