Change color behind app on rotation - ios

When an iOS app rotates it will reveal a black background when the app is between portrait and landscape. Is it possible to change this color from the default black to white? Changing the UIWindow's background color will not help. Here is an example of the black background in Safari during rotation:

I have done something similar but I couldn't find the source now, but here is the idea:
Create and add a significantly larger view as backing view and center it.
Add the UIWebView as subview of this large view whose background is white.
Re-position the center of the UIWebView, too.

You can do this way:
Add a UIViewController and set it as initial VC (in screenshot it is MainVC).
Add two UIViewContainer: first for holding your background view , and second for your other vcs.
Override viewDidLayoutSubviews in implementation file of background VC (in this case the .m file of red VC)
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
//Some hardcode :)
self.view.frame = CGRectMake(-100, -100, 1136, 1136);
}
After doing this you will have something like this:
I know this is not the best solution, but you can do this way until you find the best one.

I got the same issue. As I understand that you want to remove the black background. The easiest solution that I used is set you window clipsToBounds = true instead of your rootViewController.
window?.clipsToBounds = true

You can solve the problem by adding empty general view controller with oversized bounds into your root viewController and make it the lowest in the view hierarchy:
CGFloat length = 2*MAX(rootViewController.view.bounds.size.height, rootViewController.view.bounds.size.width);
UIView *oversizedBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, length, length)];
oversizedBackgroundView.center = vc.view.center;
oversizedBackgroundView.backgroundColor = [UIColor whiteColor];
rootViewController.view.clipsToBounds = NO;
[rootViewController.view addSubview:oversizedBackgroundView];
[rootViewController.view sendSubviewToBack:oversizedBackgroundView];
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
The key point here is to set clipsToBounds to NO

Related

How do I add a subview to UIStatusBar in Theos?

I know it sounds like this question has a simple answer, but hear me out. Although UIStatusBar is a subclass of UIView, you can't use the addSubview method to add a subview to it because it doesn't use it. The same goes for UIStatusBarWindow. Neither the view or window have a viewcontroller, so I can't hook into that in any way.
Here is the relevant section of code. The line where I call the addSubviews method on self is the issue, because addSubviews isn't a method of UIStatusBar.
#import <CoreGraphics/CoreGraphics.h>
#interface UIStatusBar : UIView
#end
%hook UIStatusBar
- (void)layoutSubviews {
//Round corners under status bar
CGFloat radius = 15;
CGRect wholeScreen = [[UIScreen mainScreen] bounds];
UIView *roundedCorners = [[UIView alloc] initWithFrame: CGRectMake(-radius, 20-radius, wholeScreen.size.width+2*radius, wholeScreen.size.height-20+2*radius)];
roundedCorners.layer.borderWidth = radius;
roundedCorners.layer.cornerRadius = 2*radius;
roundedCorners.layer.borderColor = UIColor.blackColor.CGColor;
roundedCorners.userInteractionEnabled = NO;
[self addSubView:roundedCorners];
}
%end
Is there another way I can add the subview? The reason I'm trying to do it this way is so that, whenever the status bar is hidden, my roundedCorners view is also hidden. I could hide it whenever the status bar is hidden, but due to different apps using many different methods of hiding the status bar that doesn't work out as well as I hoped.
I think a solution here is to use the notifications delivered whenever the status bar's height changes.
Using either/both:
UIApplicationWillChangeStatusBarFrameNotification
UIApplicationDidChangeStatusBarFrameNotification
or you can also use the AppDelegate methods that get called when the status bar changes frame:
-application:willChangeStatusBarFrame:
-application:didChangeStatusBarFrame:
You can in these methods adjust your rounded corners according to the status bar's new frame. Hopefully this solves you problem!

iOS hidesBarsOnSwipe status bar background color

When I swipe and hide the navigation bar with the hidesBarsOnSwipe property the status bar has a clear background. How can I set the background of the status bar to the same color as the navigation bar? Here are a few pictures showing my problem, this is all contained in a UITableViewController.
Separate
Separate picture, looks like one big one.
I've come across the same issue, and was able to solve it. I'm fairly new to iOS dev, and I don't imagine this solution to be foolproof. I couldn't find any good answers elsewhere, so here's how I overcame it:
I converted from a UITableViewController over to UIViewController with a nested UITableView. Note, double check that the delegate to the child tableview is set to the UIViewController.
I Added a view with a height of 20px and a background colour that you want to set as the "background" to the status bar. Set the constraints on that view as follows:
On your table view, set the constrains to be basically full screen. One important note here, the top constraint is to "Top Layout Guide.Top" and not to "Top Layout Guide.Bottom". By default I believe this constraint ties to the bottom. Double clicking on the constraint allows you to adjust it to the top. Without this, any table header cells weren't positioned properly for me
Hope that helps.
Adding to George Huber's answer. I solved this issue programmatically by adding a 20pt height UIView as a subview of the navigationController's view property -- in viewDidLoad method.
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *statusBarBG = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 20)];
statusBarBG.backgroundColor = [UIColor navBar];
[self.navigationController.view addSubview:statusBarBG];
// REST OF CODE
}
Per skg's answer, I add a relative height for status bar according to iOS version.
self.navigationController.hidesBarsOnSwipe = true;
// add a UIView as subView to navigationController
CGFloat statusBarHeight;
if (#available(iOS 13, *)) {
NSArray *windows = UIApplication.sharedApplication.windows;
UIWindow *keyWindow = nil;
for (UIWindow *window in windows) {
if (window.isKeyWindow) {
keyWindow = window;
break;
}
}
statusBarHeight = keyWindow.windowScene.statusBarManager.statusBarFrame.size.height;
NSLog(#"statusBarHeight: %f", statusBarHeight);
} else {
statusBarHeight = UIApplication.sharedApplication.statusBarFrame.size.height;
}
UIView *statusBarBG = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), statusBarHeight)];
statusBarBG.backgroundColor = [UIColor systemBackgroundColor];
[self.navigationController.view addSubview:statusBarBG];

Set background image for entire iOS app

I'm trying to set a background image for the entire app following this suggestions: set background image for entire iPhone / iPad app
But in iOS 7 (don't know about other versions) it doesn't work well at all.
I've created a simple repository so you can understand better what's is going on.
There are some glitches in the transitions.
When you tap on a row in the first view, the second view is pushed into the navigation controller but there's a weird effect. It seems that the rows transparency played into this.
Also the other problem is when you turn back to the previous view controller there's a subtle shadow of the view controller that is popped from the navigation stack. As I stated before you can get what I mean by running the simple Xcode project.
Repo: https://github.com/socksz/FixedBackgroundImage
Any ideas? I've already tried to set the background image for each controller but it isn't what I want because in that way the image "overlaps" the previous background image and it's not the desired effect.
Hope I explained well.
EDIT 1
It seems that the problem occurs because of the way iOS 7 manages the transitions between two view controllers. In you are in the second view controller and try to turn to the previous controller with the swipe gesture you can see that as you begin the gesture the first controller appears below the second controller (the controller you're seeing) and, since the UITableViewCells have transparent backgrounds, you already see the first controller. Actually I'm afraid that there's not a solution. What a pity that I cannot have a fixed background image without setting the background image on each controller.
I had a requirement in an iPhone app to set the background image of a page based on a user's preferences. The way I dealt with it was to add a UIImageView with the background image as a sub-view of the view, like so -
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background-image"]];
bgImageView.frame = self.view.bounds;
[self.view addSubview:bgImageView];
[self.view sendSubviewToBack:bgImageView];
I cloned your Github repository and added the above piece of code in viewDidLoad of both the view controllers. I also added the following line of code in the same method -
self.tableView.opaque = NO;
I commented out the code in didFinishLaunchingWithOptions where you set the background color. With these changes, the artifacts while navigating between view controllers are gone. I tested with iPhone Retina (3.5-inch) as well as iPhone Retina (4-inch) simulators.
The reason why the artifacts are seen while navigating to and from the ViewController in the storyboard require some investigations. My suggestion may or may not work for your requirements, but, you can try this as a solution.
P.S. The method requires some tweaks to autolayout constraints.
You Just have to write only one line in
appdelegate.m file's applicationdidFinishLaunchingWithOptions: method
[self.window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"MainBackground.png"]]];
and put below line in every screen's viewDidLoad method
[self.view setBackgroundColor:[UIColor clearColor]];
I did not find a way to put this globally. However, and you will probably find this useful for static/fixed images (instead of the moving images you get when you set the backgroundColor property), Use the backgroundView property for every screen.
self.tableView.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.jpg"]];
I did it myself by creating a UtilTableViewController which does all theme and custom things I need it to do, putting this code there, then subclassing all my views. It's not a globally set image, but I only have to set it once and all of my TableViews will use it.
Put this code in your appdelegate.m file applicationDidFinishLaunching method.
UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:rootViewController];
windowBackground=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"background_window.png"]];
windowBackground.frame=CGRectMake(0, 0, 320, 568);
[window addSubview:windowBackground];
[windowBackground release];
window.frame = CGRectMake(0, 0, window.frame.size.width,568);
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
Add this code in every viewController class viewDidLoad method.
self.view.backgroundColor = [UIColor clearColor];
Late post...
If you are using a NavigationController you might try overriding the TopViewController "get" portion to automatically set the BackGroundColor to your image. Appologies, we use Xamarin which converts from C# to objective C for us (not sure of the specific syntax). In C# it will look something like this within your NavigationController class.
public override UIViewController TopViewController
{
get
{
if (base.TopViewController.View.BackgroundColor != "Your Image")
{
base.TopViewController.View.BackgroundColor = "Your Image";
}
return base.TopViewController;
}
}
Write this code in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"YourImageName.png"]];
self.window.backgroundColor = background;
you can set the background image through below code ... wew can put this code in viewdidload method in viewcontroller.m file
[self.window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"bg.png"]]];

UINavigationController pushViewController pauses/freezes midway through

I am pushing a view controller via:
[self.navigationController pushViewController:[[UIViewController alloc] init] animated:YES];
But the animation lags/pauses a for half a second mid way through. The animation is not complete. Here's the gif;
With out more detail I can think of 2 possible problem with that.
Is there Shadow added in code to the view that will be covered by the new ViewController. If it is the case, use ShadowPath or an translucent view instead (the property Shadow is expensive while animating, been there done that)
Is the backgroundColor of new ViewController "clearColor" ? I've seen strange rendering problem with that kind of thing.
Try:
UIViewController *vc = [[UIViewController alloc] init];
vc.view.backgroundColor = [UIColor whiteColor];
[self.navigationController pushViewController:vc animated:YES];
That is the 2 possible problems I can think of the top of my head with so few detail.
Never rely on the default background color, it has change with iOS version and is not consistant across controls and can even be different if the view is created in code or from a Xib (in the same iOS version).
In app delegate, set your window's background color to white.
window?.backgroundColor = .white
Also in the the pushed view controller, set its view to white.
view.backgroundColor = .white
I experienced the same issue when programmatically embedding my view controller in a UINavigationController.
While setting the background color as suggested by VinceBurn solved the pausing, it made the entire animation white, fading in the actual content only when the animation finished.
For me the problem was solved by making sure the content was correctly sized in -viewDidLoad.

How do I get a UINavigationController to NOT change its view size when setting the translucent property?

I have an app where up until now I've been using a UINavigationController with a UINavigationBar that has its property translucent = YES. This means the UINavigationController's content view (i.e. the views from the view controllers you push) to be full-screen (minus status bar).
However, if you set the navigationBar.translucent = NO, this container view becomes 44pt shorter, as I suppose Apple has assumed you don't need any content under an opaque navigationBar.
... except if you're doing what we're doing and are employing a navigationBar that scrolls away (see This Post on how to do that) So I'd like to know if this is possible.
I want to have translucent = NO, but have everything behave as if it were still set to YES. I like the functionality of the translucent = YES, but I don't actually want the bar to be made translucent by UIKit.
What worked for me was to add
extendedLayoutIncludesOpaqueBars = true
in
viewDidLoad
something like this
override func viewDidLoad() {
super.viewDidLoad()
extendedLayoutIncludesOpaqueBars = true
}
Hope it will work for you as well
It's not necessarily a good answer but you could just offset your view that high if you're not translucent.
//This won't take into account orientation and probably other details
if(!self.navigationController.navigationBar.isTranslucent)
{
self.view.frame = CGRectMake(0,0,-44,self.view.bounds.size.height);
}
You could put that in your viewDidLoad or viewWillAppear and if you have a bunch of view controllers you can just subclass them all and put your logic in the subclass.
I found a solution that works, although it is indeed a bit of a hack.
The idea is to give the translucent nav bar an opaque backing. Unfortunately I'm not happy with the solution in that it's dirty and not encapsulated and introduces some potential issues, but i AM happy because it got the job done.
In my Application's base view controller class (i.e. MyViewController : UIViewController), in the viewDidLoad method, I instantiate a new ivar UIView *_navigationBarBG and give it the same frame as self.navigationController.navigationBar. I then set it's backgroundColor property to [UIColor whiteColor] although this is how you achieve some more tint I guess. [EDIT:If you wanted to be a purist (color values remaining exactly as they come from the .psd), you could make the _navigationBarBG a UIImageView and use your custom background there, and the background of the actual UINavigationBar you set to draw clear (or stretch a 1px transparent image if you wanted to use a typical 'change your navigation bar using an image' recipe that's somewhere on the internet)]
if(self.navigationController)
{
_navigationBarBG = [[UIView alloc] initWithFrame: self.navigationController.navigationBar.frame];
_navigationBarBG.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_navigationBarBG];
}
THEN, (and this is the crappy part, but I don't see any other way), I add this view as a subview. BUT, whenever you would normally make a call to [self.view addSubview: anyView], you have to make sure you call [self.view insertSubview: anyView belowSubview: _navigationBarBG];
if (_navigationBarBG)
[self.view insertSubview: anyView belowSubview:_navigationBarBG];
else
[self.view addSubview: anyView];
If you forget that, these added views will slide under your navbar background and look weird. So you need to know that this is a source of error.
WHY AM I DOING THIS? Again you might ask... I want to be able to have a scrolling navigation bar that scrolls out of the way when you scroll down your table view, thereby giving the user more screen space. This is done by using the scrollView delegate (scrollViewDidScroll:) and also viewWillAppear:
// FIRST DEAL WITH SCROLLING NAVIGATION BAR
CALayer *layer = self.navigationController.navigationBar.layer;
CGFloat contentOffsetY = scrollView.contentOffset.y;
CGPoint newPosition;
if (contentOffsetY > _scrollViewContentOffsetYThreshold && self.scrollingNavigationBarEnabled) {
newPosition = CGPointMake(layer.position.x,
22 - MIN((contentOffsetY - _scrollViewContentOffsetYThreshold), 48.0)); // my nav bar BG image is 48.0 tall
layer.position = newPosition;
[_navigationBarBG setCenter: newPosition]; // if it's nil, nothing happens
}
else
{
newPosition = kNavBarDefaultPosition; // i.e. CGPointMake(160, 22) -- portrait only
layer.position = newPosition;
[_navigationBarBG setCenter: newPosition]; // if it's nil, nothing happens
}
I was looking for an answer to this as I wanted my subviews to be at (0,0) and not (0,44)(in reference to the Screen bounds), but I could not find an answer on how to set this in the NavigationController, which I thought would be an included property.
What I ended up doing that was very simple is adding a subview to the navigation controller that was the width and height of the Navigation Bar, but then insert the subview below the Navigation Bar.
Now the setting is Translucent = YES, but it still appears solid and the subviews behave how I want.
EDIT: After re-reading your original post, I suppose if you're going to be rolling the nav bar away, you'll have to take into account hiding and showing the new subview as you do the same with the nav bar

Resources