iOS - UIView Always on Front without BringSubviewToFront - ios

Can I have some UIView which will always appear on top in iOS?
There are lots of addSubview in my project but I need to have one small view which will always appear. SO is there any other option than
[self.view bringSubViewToFront:myView];
Thanks

One more option (especially if you want to overlap several screens, with logo for example) - separate UIWindow. Use windowLevel to set the level of new window.
UILabel *devLabel = [UILabel new];
devLabel.text = #" DEV ";
devLabel.font = [UIFont systemFontOfSize:10];
devLabel.textColor = [UIColor grayColor];
[devLabel sizeToFit];
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
static UIWindow *notificationWindow;
notificationWindow = [[UIWindow alloc] initWithFrame:
CGRectMake(screenSize.width - devLabel.width, screenSize.height - devLabel.height,
devLabel.width, devLabel.height)];
notificationWindow.backgroundColor = [UIColor clearColor];
notificationWindow.userInteractionEnabled = NO;
notificationWindow.windowLevel = UIWindowLevelStatusBar;
notificationWindow.rootViewController = [UIViewController new];
[notificationWindow.rootViewController.view addSubview:devLabel];
notificationWindow.hidden = NO;

Another option is set layer.zPosition of your UIView.
You need to add
#import <QuartzCore/QuartzCore.h>
Framework to your .m file.
And set such like
myCustomView.layer.zPosition = 101;// set maximum value as per your requirement.
For more information about layer.zPosition read this documentation.
Discussion
The default value of this property is 0. Changing the value of this property changes the the front-to-back ordering of layers onscreen. This can affect the visibility of layers whose frame rectangles overlap.

The other option is to add other subviews below this always-on-top subview. For example:
[self.view insertSubview:subview belowSubview:_topSubview];
There's no solution with Interface Builder if you search for this kind. It should be done programmatically. If you don't want to use bringSubviewToFront: everytime, just insert other subviews below this one.

Many times your view did not appear in viewDidLoad or, if your view comes from parentViewController (for example in many transitions like modal segue..) your can see parentViewController only in viewDidAppear so:
Try to put bringSubviewToFront in :
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.view bringSubViewToFront:myView];
// or if your view is attached in parentViewController
[self.parentViewController.view bringSubViewToFront:myView];
}
Good luck!

Related

AdMob banner view background color is black, want white or transparent

I can't figure out how to change my AdMob banner's background color when the ad doesn't fit. Is this possible? The code below wouldn't work for me.
self.ad.backgroundColor= [UIColor whiteColor];
This is actually achieved through AdMob's dashboard.
Go to AdMob.com
Select Monetize on the top toolbar
Select the application on the left side bar that you want to change the background color for
Select the Ad Unit of the banner
In the Text ad style drop down menu select Customized
Select Background color and change it to whichever color you desire
Select Save and you're all done
It may take a few minutes for changes to appear in your application.
You could add your GADBannerView to another UIView that you set the backgroundColor of. This would require using AdMob's default sizes to make sure that image banners fit properly. The down fall of this is that text based ads will be restricted to this size also instead of filling the entire ad area.
For example:
#import "ViewController.h"
#import GoogleMobileAds;
#define ADUNIT_ID #"yourAdUnitID"
#interface ViewController () <GADBannerViewDelegate> {
GADBannerView *admobBanner;
UIView *backgroundView;
}
#end
#implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
// Create our AdMob banner
admobBanner = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner];
admobBanner.adUnitID = ADUNIT_ID;
admobBanner.rootViewController = self;
admobBanner.delegate = self;
[admobBanner loadRequest:[GADRequest request]];
// Create a view to put our AdMob banner in
backgroundView = [[UIView alloc]initWithFrame:CGRectMake(0,
self.view.frame.size.height - admobBanner.frame.size.height,
self.view.frame.size.width,
admobBanner.frame.size.height)];
// Hide view until we have an ad
backgroundView.alpha = 0.0;
// Set to color you require
backgroundView.backgroundColor = [UIColor redColor];
// Add our views
[self.view addSubview:backgroundView];
[backgroundView addSubview:admobBanner];
// Center our AdMob banner in our view
admobBanner.center = [backgroundView convertPoint:backgroundView.center fromView:backgroundView.superview];
}
-(void)adViewDidReceiveAd:(GADBannerView *)adView {
NSLog(#"adViewDidReceiveAd");
[UIView animateWithDuration:0.5 animations:^{
backgroundView.alpha = 1.0;
}];
}
-(void)adView:(GADBannerView *)adView didFailToReceiveAdWithError:(GADRequestError *)error {
NSLog(#"adView:didFailToReceiveAdWithError: %#", [error localizedDescription]);
[UIView animateWithDuration:0.5 animations:^{
backgroundView.alpha = 0.0;
}];
}
iPhone / iPad
You should insert the ad in a container view.
Then, in the delegate that is called when the ad is loaded adjust the container width to be the same size as the ad view
You need to start by figuring out what views compose the ad view. Probably you are changing the view's color, but it has another view on top of it that contains the ad, and total blocks the parent view.
Start by placing a breakpoint in your code (I often place it in -viewDidAppear:) and once in there, type this in the debugger to see the subviews of the ad view:
po self.ad.subviews
My guess is that you'll see one subview that stretches the full length of the main window. It may even be a UIImageView that contains the ad image. But you can continue to look at the subviews of the subviews, either in the debugger or by adding code in -viewDidAppear:.
This is probably view you want to mess with. In -viewDidAppear:, try adding code to color the subview's background instead:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
UIView *adSubview = self.ad.subviews.firstObject;
adSubview.backgroundColor = [UIColor whiteColor];
}
This code should be safe even if the ad or its subviews property returns nil.
I think you should fill rect of UIView same Admob size.
bannerView = GADBannerView(frame: rectBanner)
bannerView.adUnitID = "xxxxxxxxxxxxxxxxxx"
bannerView.rootViewController = self
frame size(rectBanner) should be match with Admob Size.

create a UILabel when view appears and destroy it when view disappears

For the tutorial for my app, I am trying to create a UILabel that drifts across the displayed screen when a view appears and then is destroyed so that if the user comes back to that view during the tutorial, the UILabel will be created anew and again drift across the page. Here's a sample of one of the custom view controllers I am displaying with my UIPageViewController:
//this is just a custom UILabel with some padding
#property (nonatomic) PaddedLabel *directionsLabel;
//I have tried setting UILabel to nil or removing it from view
-(void)viewWillDisappear:(BOOL)animated
{
NSLog(#"view is disappearing");
//this does not remove the label
self.directionsLabel = nil;
//nor does this
[self.directionsLabel removeFromSuperview];
}
- (void)viewDidAppear:(BOOL)animated
{
[self messageTutorial];
}
- (void)messageTutorial
{
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
PaddedLabel *directionsLabel = [[PaddedLabel alloc] initWithFrame:CGRectMake(_width/2, _height/2, 100, 100)];
directionsLabel.text = #"test";
CGRect f = directionsLabel.frame;
f.size = size;
directionsLabel.frame = f;
directionsLabel.center = self.view.center;
f = directionsLabel.frame;
f = directionsLabel.frame;
f.origin.y = .1*height;
directionsLabel.frame = f;
[self.view addSubview:directionsLabel];
[UIView animateWithDuration:TUTORIAL_DISAPPEAR_TIME animations:^{
directionsLabel.alpha = .5;
CGRect f = directionsLabel.frame;
f.origin.y = height - f.size.height*1.4;
directionsLabel.frame = f;
NSLog(#"animating");
} completion:^(BOOL finished) {
[directionsLabel removeFromSuperview];
//this also doesn't actually remove the label
}];
}
The problem is that if the user pages back to see this view she now sees a new label and the old one, so that if you page back and forth back and forth you end up with many many labels all saying the same thing, in different stages of progressing across the screen.
How can I remove the UILabel when the view disappears and add/create a new one when the view appears/reappears?
Thank you.
The code in your viewWillDisappear method is backwards. You need:
- (void)viewWillDisappear:(BOOL)animated {
NSLog(#"view is disappearing");
[self.directionsLabel removeFromSuperview];
self.directionsLabel = nil;
}
As you had it, setting self.directionsLabel to nil before trying to remove it results in a no-op.
Also, be sure to set self.directionsLabel when you create the label.
Instead of setting your label to nil and effectively destroying the label object (assuming automatic reference counting is on) rather use the following method to hide and show the label as and when your need it.
[self.directionsLabel setHidden:YES]; // hides it
[self.directionsLabel setHidden:NO]; // shows it
You've got the right idea setting objects you're not using to nil and removing them from the super view but it's over kill. A UILabel object uses a negligible amount of memory and you're better off creating the object once and then changing it's properties as you need to.
You don't seem to be setting self.directionsLabel to anything when you create the directionsLabel inside the messageTutorial method. It is a local instance of the label there. You should set it in the method somewhere.
Afterwards, removing it from the superview in viewWillDisappear will work (tested to verify).

Is UICollectionView.backgroundView broken

I'm writing something relatively simple, or so I thought.
Firstly, the code, for which I'm trying to place an image on the background of the UICollectionView if there are no results returned from my server. The image is 200 by 200:
UIView *myView = [[UIView alloc] initWithFrame:self.view.bounds];
CGRect myViewSpace = self.view.bounds;
CGFloat myX = (myViewSpace.size.width /2.0) - 100;
CGFloat myY = (myViewSpace.size.height /2.0) - 100;
UIImageView *imView = [[UIImageView alloc] initWithFrame:CGRectMake(myX, myY, 200, 200)];
imView.image = [UIImage imageNamed:#"imNotHome"];
[myView addSubview:imView];
myCollectionView.backgroundView = myView;
Once there are results, I want to be able to remove it.
I thought it'd be as simple as placing the following, before I reloaded the UICollectionView:
[myCollectionView.backgroundView removeFromSuperview];
However, it appears to be doing nothing.
Am I missing something?
Thanks in advance!
It should be done this way instead:
myCollectionView.backgroundView = nil;
Explanation: You should unset the UICollectionView's background in the same way as you set it. You didn't set it by manipulating the view hierarchy, but by setting the background property. You did call addSubview in the previous line, but that was to add a subview to your background view, not to add the background view itself.
Edit:
There is a very good article about this UICollectionView bug here:
http://blog.spacemanlabs.com/2013/11/uicollectionviews-backgroundview-property-is-horribly-broken/
The solution the author gives is to reset the background to an empty opaque view:
UIView *blankView = [UIView new];
blankView.backgroundColor = [UIColor whiteColor];
[myCollectionView.backgroundView removeFromSuperview];
myCollectionView.backgroundView = blankView;
Also, the author recommends not using the backgroundView property at all but doing it yourself:
Frankly, I think the best solution is to just ignore the backgroundView property all together. Instead, make the collection view’s background clear, and implement your own backgroundView; just throw a view behind the collection view.

Shadow in separate view from UIImage for dynamic adjustments

I would like to obtain this effect (http://stackoverflow.com/questions/7023271/how-to-adjust-drop-shadow-dynamically-during-an-uiimageview-rotation) but from a more complex image than just a red square ! If the link ever gets broken, it's a about how to adjust drop shadow dynamically during an UIImageView rotation.
So I tried implementing something but I just can't get the shadow in a separate layer... Here is my code, very simple, but doesn't work:
// here is my code
- (void)viewDidLoad {
[super viewDidLoad];
testView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:#"handNoShadow.png"]];
testViewShadow = [[UIView alloc] initWithFrame:testView.frame];
testViewShadow.layer.shadowPath = [[testView layer] shadowPath];
testViewShadow.layer.shadowOpacity = 1.0;
testViewShadow.layer.shadowOffset = CGSizeMake(10, 10);
testViewShadow.layer.shadowRadius = 5.0;
[self.view addSubview:testViewShadow];
[self.view addSubview:testView];
}
PS: i did #import
I do get an image but no shadow... =(
Any lead, help, code, link... is welcome !
Thanks
possible cause:
your testViewShadow.clipToBounds property is set to YES (should be NO)
your testViewShadow do the drawing of the shadow correctly but another UIView is on top and mask it. Check your Z order. Either the order in Storyboard/Nib file (or the order you added the subviews programmatically). Last in the list (or last one added) is on top. For my app I had to put the UIView that need a shadow last so that no other view mask it.

Setting rootViewController on UIWindow changes UIScrollView with paging layout

UPDATE
It turns out that the code below is not actually the problem. In my app delegate I am doing:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] init];
self.window.rootViewController = self.viewController;// <-- this does not work
//[self.window addSubview:self.viewController.view]; // <-- this works
[self.window makeKeyAndVisible];
return YES;
}
If I remove the statement "self.window.rootViewController = self.viewController" and just add the viewController's view to the window, it works. Can anyone explain this? Does setting the rootViewController on the window constrain the child's bounds? I have tried to go through the docs, but it doesn't mention much about this.
ORIGINAL POST
I am having trouble adding padding to pages in a UIScrollView. I am basically trying to setup a simple scroll view that shows UIViews in different pages separated by a predefined padding (kind of like the Photos app without photos). I have been trying to follow Apple's ScrollView example from WWDC 2010 and their sample app PhotoScroller but always come up with padding showing in the view. The app currently hides the status bar and adds 1 view controller to the window. To make things simple, each of the pages should show a UIView that is colored green, while the space where there is padding is yellow. You should only see the yellow when the user is scrolling. Here are the first 3 pages:
I have a single class level field called pagingScrollView declared in the .h file. In my single view controller, I am basically just trying to follow what the sample code is doing.
#define PADDING 10
#define PAGE_COUNT 3
- (void)loadView
{
CGRect pagingScrollFrame = [[UIScreen mainScreen] bounds];
pagingScrollFrame.origin.x -= PADDING;
pagingScrollFrame.size.width += (2 * PADDING);
pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollFrame];
pagingScrollView.pagingEnabled = YES;
pagingScrollView.backgroundColor = [UIColor yellowColor];
pagingScrollView.contentSize = CGSizeMake(pagingScrollFrame.size.width * PAGE_COUNT, pagingScrollFrame.size.height);
self.view = pagingScrollView;
for(int i = 0; i < PAGE_COUNT; i++) {
CGRect frame = [self frameForPageAtIndex:i];
UIView *page = [[UIView alloc] initWithFrame:frame];
page.backgroundColor = [UIColor greenColor];
[pagingScrollView addSubview:page];
}
}
- (CGRect)frameForPageAtIndex:(NSUInteger)index {
CGRect bounds = pagingScrollView.bounds;
CGRect pageFrame = bounds;
pageFrame.size.width -= (2 * PADDING);
pageFrame.origin.x = (bounds.size.width * index) + PADDING;
return pageFrame;
}
The pagingScrollFrame has a width of 340, so (I thought) that scroll view would be broken up into pages of 340 pixels. What am I missing?
Looking at this very briefly, it appears that you are doing things fairly correct, except for the setting of your content size. You set:
pagingScrollView.contentSize = CGSizeMake(pagingScrollFrame.size.width * PAGE_COUNT, pagingScrollFrame.size.height);
This would be correct if each of your pages was truly right next to each other, but as you are adding a 10pt pad between each view, you should have something like:
pagingScrollView.contentSize = CGSizeMake(pagingScrollFrame.size.width * PAGE_COUNT + PADDING * (PAGE_COUNT - 1), pagingScrollFrame.size.height);
This should correct your problem and cause the yellow to not be in the visible area.
The reason the paging is off is because setting the RootViewController on the window is apparently doing something behind the scenes (what that is, I don't know). To fix is, I use the old way of adding a view to the window.
[self.window addSubview:self.viewController.view];
If you think you know how to fix it while setting the RootViewController, please let me know!

Resources