ios - Wanting to clean up my viewDidLoad Method - ios

.
Hi,
I am wanting to clean up my viewDidLoad method as I it is growing in number of lines of code and it is getting messy.
Now my UI is build programmatically, because I want to learn that way of doing things.
So I read on this SO Post that I can set the UI items in a seperate -9void) method and then link to that void by using [self method]
Now when I use that way, it does not seem to work for me.
like, if I want to set the back ground color this will work:
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithRed:0.0 green:0.2 blue:0.38 alpha:1.0];
}
but this does not:
- (void)viewDidLoad
{
[super viewDidLoad];
[self backgroundColor];
// Do any additional setup after loading the view.
}
-(void)backgroundColor
{
UIView *backgroundView = [[UIView alloc] init];
backgroundView.backgroundColor = [UIColor colorWithRed:0.0 green:0.2 blue:0.38 alpha:1.0];
}
Am I misunderstanding this??
Thanks in Advance:-)

I think you're misunderstanding the nature of views/view controllers.
In this line:
self.view.backgroundColor = [UIColor colorWithRed:0.0 green:0.2 blue:0.38 alpha:1.0];
self refers to the view controller you are loading, and view refers to the view that is owned by that view controller. So when you set self.view.backgroundColor, you are setting the background color for the view that will be presented by the view controller you are displaying. Therefore that works.
Your other method, on the other hand, doesn't do that:
UIView *backgroundView = [[UIView alloc] init];
That line creates an entirely new UIView instance, and sets its background color. This is a brand new view, and is NOT the same view which is referenced earlier by self.view.
If you really want to have a separate function that changes the background color for you, write it like this:
-(void) setBackgroundColor
{
self.view.backgroundColor = [UIColor colorWithRed:0.0 green:0.2 blue:0.38 alpha:1.0];
}
Then you're actually modifying the view that belongs to the view controller, and the change should actually display.
I would also suggest that this function isn't very useful; you're creating a function that encapsulates a single line of code that will never change. There's really not much point in making a function for that. A more useful implementation might be:
-(void) setBackgroundColor:(UIColor)newColor
{
self.view.backgroundColor = newColor;
}
To use this method, you would write the following line in your viewDidLoad method:
[self setBackgroundColor:[UIColor colorWithRed:0.0 green:0.2 blue:0.38 alpha:1.0]];
And then you could call setBackgroundColor again whenever you wanted, to change the background color to different values.

Your backgroundColor method in the second instance is just setting the background colour on some different UIView object that gets created and then leaked. (The method name is also not great -- its suggests a property getter, but doesn't actually get a property or return anything.)

The problem here is you method backgroundColour isn't declared before it's used. So there are 3 ways to get around this
Move your backgroundColor method above viewDidLoad
Declare the backgroundColour as a public method in the #interface
Create a private interface and declare backgroundColour
No.3 Example:
#interace MyViewController (private)
- (void)backgroundColor;
#end
Note if your interface is inherits from anything still write the private interface as above.
Also don't create a new view just perform [self.view setBackgroundColor:[UIColor redColor]];

Related

Wrong tint color for UIRefreshControl

I am using a UIRefreshControl in my UITableViewController to refresh the content. I use the following code in my -viewDidLoad method to add it to my table.
UIRefreshControl *refresh = [[UIRefreshControl alloc] init];
//[refresh setAttributedTitle:[[NSAttributedString alloc] initWithString:#"Reloading" attributes:#{NSForegroundColorAttributeName : [UIColor lightGrayColor]}]];
[refresh setTintAdjustmentMode:UIViewTintAdjustmentModeNormal];
[refresh setTintColor:[UIColor colorFromHex:0xff8900]];
[refresh addTarget:self action:#selector(refresh) forControlEvents:UIControlEventValueChanged];
[[refresh layer] setZPosition:-1.0f];
//if (iOS_AT_LEAST(10)) {
// [self setRefreshControl:refresh];
//} else {
[self setRefresher:refresh];
[[self tableView] insertSubview:[self refresher] atIndex:0];
//}
I created a category for UIColor so it can accept hex values. I use this in the complete application and on another location I get #ff8900 back which is correct. But setting the tint color on the UIRefreshControl gives me #fb6a2e which is odd. The same happens when I try a default color, for example [UIColor lightGrayColor] gives me another tint than giving the same color to another view.
I have found the following post: UIRefreshControl tint color doesn't match given color. I tried to put it into an animation block and the hack given in the answer, but with no luck.
What do I do wrong here?
Another side question. When setting the attributed title, the spinner has a greater space from the top. If this happens, the title will be below the cell if only one is available. Is there a way to move the string up?

should adding a UILabel in a custom UIView be in drawRect or initWithFrame / custom setup method

I'm not a full-time iOS dev and have to make some changes to someone else's code. We have a custom view where a UILabel is added in drawRect like this (edited for brevity):
- (void)drawRect:(CGRect)rect
{
UILabel *myLabel=[[UILabel alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 100.0f, 30.0f)];
myLabel.text=#"here is some text";
[self addSubview:myLabel];
}
I have never really seen this and thought that drawRect was ONLY for adding drawing operations (and have only seen UIBezierPaths). Should this be moved to initWithFrame (or a common setup method like setupMyView). Or is ok to leave in drawRect? Is there anything besides custom drawing that should be in drawRect?
Sorry for asking a somewhat basic question but even reading the Apple docs leave a bit to be desired.
Unless there is a very good reason to setup the view's subviews from within the drawRect method (I can think of none) I would strongly suggest leaving drawRect as purely a drawing method and move that addSubview stuff out of there! I would suggest overriding the -init method that is currently used to initiate the parent view. For example:
-(instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:(CGRect)frame]) {
UILabel *myLabel=[[UILabel alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 100.0f, 30.0f)];
myLabel.text=#"here is some text";
[self addSubview:myLabel];
}
return self;
}
I would definitely not do anything like that in drawRect. One method I've used in the past is to add it to layoutSubviews, because at that point the view is aware of the true bounds/frame. You'll want to ensure that you only generate the view stuff once, as layoutSubviews is called many times, such as on rotation. I usually do something like the following, where _viewGenerated is an instance variable:
- (void)layoutSubviews {
[super layoutSubviews];
if (!_viewGenerated) {
[self generateView];
_viewGenerated = YES;
}
}
- (void)generateView {
// do everything with any labels, images, etc., here...
}

Cannot set UISearchBar's translucent property to NO

I am trying to set the barTintColor of a UISearchBar without translucency. However, setting the translucent property doesn't seem to do anything. I have reproduced the issue in a bare-bones Xcode project here.
self.searchDisplayController.searchBar.translucent = NO;
self.searchDisplayController.searchBar.barTintColor = [UIColor redColor];
The red color above is not the same as [UIColor redColor] in UIViews that are not translucent. I know about the workaround involving setting a background image on the search bar, but the above code should work as well.
I downloaded your code and find solution for, add one method name is removeUISearchBarBackgroundInViewHierarchy and set searchBar.backgroundColor as redColor.
- (void)viewDidLoad
{
[super viewDidLoad];
self.searchDisplayController.searchBar.translucent = NO;
[self removeUISearchBarBackgroundInViewHierarchy:self.searchDisplayController.searchBar];
self.searchDisplayController.searchBar.backgroundColor = [UIColor redColor];
}
- (void) removeUISearchBarBackgroundInViewHierarchy:(UIView *)view
{
for (UIView *subview in [view subviews]) {
if ([subview isKindOfClass:NSClassFromString(#"UISearchBarBackground")]) {
[subview removeFromSuperview];
break; //To avoid an extra loop as there is only one UISearchBarBackground
} else {
[self removeUISearchBarBackgroundInViewHierarchy:subview];
}
}
}
Please set image in search bar like this it will solve your problem.
[[UISearchBar appearance] setBackgroundImage:[UIImage imageNamed:#"red"]];
Thanks.
Maybe too late for the party,
However I've done a very simple and nifty extension for Swift 3 that allows you to use the translucent property without any trouble.
It involves no use of private API or dangerous walk-through within the object.
You can download it from this Github repository.

Setting backgroundColor doesn't seem to work on a UIView

I have some code that tries to set the backgroundColor in a UIView from an Image but all I see is black screen. Here is what I did -
Have a UIViewController class and UIView class
The UIView class has a drawRect function.
In the UIViewController viewDidLoad function create the UIView class
After that the drawRect in the UIView is called.
Here is the code for the UIView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
UIColor *backGround = [UIColor colorWithPatternImage:[UIImage imageNamed:#"test.png"]];
self.backgroundColor = backGround;
}
Verified in the debugger that the backGround object is not nil after execution. Also tested the test.png file exists and I can create a UIImageView with it.
I see that the result is the same if I set the backgroundColor in the initWithFrame function instead of the drawRect()
The reason I want to set the backgroundColor is I am drawing a board game and I want to use the image as the background. That way I can just draw the lines (board) on the image.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
UIColor *backGround = [UIColor colorWithPatternImage:[UIImage imageNamed:#"test.png"]];
self.backgroundColor = backGround;
}
return self;
}
Changing the background in drawRect won't work. Any changes to it must be done in the init method.
just try out this in the viewDidLoad
-(void)viewDidLoad{
UIColor *backGround = [UIColor colorWithPatternImage:[UIImage imageNamed:#"test.png"]];
self.view.backgroundColor = backGround;
//.....
}
EDIT:
For your problem :
the drawing is being done by a layer associated with your view. It looks like, in your environment, the background-colored layer is doing its drawing before your -drawRect: method is called. When the layer redraws, your -drawRect: method also happens to be called. So, after you change the background color, you don't see any changes till the background layer draws again, which you can tell happened because your -drawRect: gets called again. (Here && Check for more info)
In drawRect method you should not set background. You have to do drawing, which you want to draw in view. Background view is separate view. If you want to set the background view within your custom view, do it in init method or else you can do it in your view controller(anywhere).

Achieving bright, vivid colors for an iOS 7 translucent UINavigationBar

iOS 7.1 UPDATE: Looks like the workaround for modifying the alpha channel in the UINavigationBar has been ignored in this update. Right now, the best solution seems to be to just 'deal with it' and hope that whatever color you choose can render a translucent effect. I am still looking into ways of getting around this.
iOS 7.0.3 UPDATE: The GitHub library we created has been updated to slightly work around this issue when using iOS 7.0.3. Unfortunately, there is no magic formula to support both colors created in iOS 7.0.2 and earlier and iOS 7.0.3. Seems like Apple improved the saturation, but at the cost of opacity (since the blurred translucency is dependant on the opacity level). I, along with a few others, are working on creating a much better fix for this.
I'm sure many people have already come across the problem where iOS 7 tends to desaturate the color of a UINavigationBar that is translucent.
My goal is to achieve a UINavigationBar with this tint color, but translucent:
However, with translucency, I'm getting this. The background view is white, which I understand will make this view a bit lighter:
Is there any way to achieve the original color while still having translucency? I've noticed Facebook has been able to get their bar to be their rich, blue color, as displayed here:
..so I know there has to be some way. Background views obviously make a difference here, but most of their content is also gray/white. It seems that regardless of whatever bar tint color you put in, you are unable to get vivid colors under translucency.
Updated with solution.
Here's the solution that I ended up coming up with. I took aprato's solution and then encompassed the custom UINavigationBar within a UINavigationController subclass. I have created a repository that has this implementation listed below, along with an example app.
////////////////////////////
// CRNavigationBar.m
////////////////////////////
#import "CRNavigationBar.h"
#interface CRNavigationBar ()
#property (nonatomic, strong) CALayer *colorLayer;
#end
#implementation CRNavigationBar
static CGFloat const kDefaultColorLayerOpacity = 0.5f;
static CGFloat const kSpaceToCoverStatusBars = 20.0f;
- (void)setBarTintColor:(UIColor *)barTintColor {
[super setBarTintColor:barTintColor];
if (self.colorLayer == nil) {
self.colorLayer = [CALayer layer];
self.colorLayer.opacity = kDefaultColorLayerOpacity;
[self.layer addSublayer:self.colorLayer];
}
self.colorLayer.backgroundColor = barTintColor.CGColor;
}
- (void)layoutSubviews {
[super layoutSubviews];
if (self.colorLayer != nil) {
self.colorLayer.frame = CGRectMake(0, 0 - kSpaceToCoverStatusBars, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + kSpaceToCoverStatusBars);
[self.layer insertSublayer:self.colorLayer atIndex:1];
}
}
#end
////////////////////////////
// CRNavigationController.m
////////////////////////////
#import "CRNavigationController.h"
#import "CRNavigationBar.h"
#interface CRNavigationController ()
#end
#implementation CRNavigationController
- (id)init {
self = [super initWithNavigationBarClass:[CRNavigationBar class] toolbarClass:nil];
if(self) {
// Custom initialization here, if needed.
}
return self;
}
- (id)initWithRootViewController:(UIViewController *)rootViewController {
self = [super initWithNavigationBarClass:[CRNavigationBar class] toolbarClass:nil];
if(self) {
self.viewControllers = #[rootViewController];
}
return self;
}
#end
iOS 7.0.3 UPDATE: As you see above 7.0.3 changed things. I've updated my gist. Hopefully this will just go away as people upgrade.
Original Answer:
I ended up with a hack combining the two of the other answers. I'm subclassing UINavigationBar and adding a layer to the back with some extra space to cover if any of the various height status bars are up. The layer gets adjusted in layout subviews and the color changes whenever you set barTintColor.
Gist: https://gist.github.com/aprato/6631390
setBarTintColor
[super setBarTintColor:barTintColor];
if (self.extraColorLayer == nil) {
self.extraColorLayer = [CALayer layer];
self.extraColorLayer.opacity = self.extraColorLayerOpacity;
[self.layer addSublayer:self.extraColorLayer];
}
self.extraColorLayer.backgroundColor = barTintColor.CGColor;
layoutSubviews
[super layoutSubviews];
if (self.extraColorLayer != nil) {
[self.extraColorLayer removeFromSuperlayer];
self.extraColorLayer.opacity = self.extraColorLayerOpacity;
[self.layer insertSublayer:self.extraColorLayer atIndex:1];
CGFloat spaceAboveBar = self.frame.origin.y;
self.extraColorLayer.frame = CGRectMake(0, 0 - spaceAboveBar, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + spaceAboveBar);
}
The behavior of tintColor for bars has changed on iOS 7.0. It no longer affects the bar's background and behaves as described for the tintColor property added to UIView. To tint the bar's background, please use -barTintColor.You can use following code to make the app work with both ios6 and ios7.
if(IS_IOS7)
{
self.navigationController.navigationBar.barTintColor = [UIColor blackColor];
self.navigationController.navigationBar.translucent = NO;
}
else
{
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
}
IS_IOS7 is a macro which is defined in pch file as follows.
#define IS_IOS7 ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
I didn't come up with this solution but it seems to work fairly well. I just added it to viewDidLoad on my subclass of UINavigationController.
Source: https://gist.github.com/alanzeino/6619253
// cheers to #stroughtonsmith for helping out with this one
UIColor *barColour = [UIColor colorWithRed:0.13f green:0.14f blue:0.15f alpha:1.00f];
UIView *colourView = [[UIView alloc] initWithFrame:CGRectMake(0.f, -20.f, 320.f, 64.f)];
colourView.opaque = NO;
colourView.alpha = .7f;
colourView.backgroundColor = barColour;
self.navigationBar.barTintColor = barColour;
[self.navigationBar.layer insertSublayer:colourView.layer atIndex:1];
One low-fi way would probably be pinning a UIView that is the height of the Navigation Bar to the top of the view behind the bar. Make that view the same color as the navigation bar but play with the alpha until you get the desired effects:
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.navigationController.navigationBar.frame), 64)];
backgroundView.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:1 alpha:.5];
[self.navigationController.view insertSubview:backgroundView belowSubview:self.navigationController.navigationBar];
UIView behind
(Changed color from lower examples to emphasis transparency. Transparency/blurring is more noticeable when in movement.)
Subclassing the UINavigationBar and placing that same view above the background but behind everything else will probably achieve similar results while being less hacky.
Another solution I've seen tossed around is playing with the alpha of the UINavigationBar:
self.navigationController.navigationBar.alpha = 0.5f;
Edit: Actually, after testing it seems like this doesn't provide the intend behavior (or any behavior):
.8 alpha
Unadjusted alpha
Obviously, you will only want to do this on iOS 7 devices. So, add some version check before you implement any of these.
Instead of creating your UIColor object in the RGB format, use HSB and increase the saturation parameter. (Credits to Sam Soffes who describes this method here)
navigationBar.barTintColor = [UIColor colorWithHue:0.555f saturation:1.f brightness:0.855f alpha:1.f];
Note: This solution is a tradeoff and doesn't work well for colors with high saturation.
To pick the HSB color from your design you can use a tool like ColorSnapper which allows you to simply copy the UIColor HSB format.
You can also try the UIColor Category (GitHub Link) from David Keegan to modify existing colors.
The problem has now been fixed by Apple in the new 7.0.3 release.
I used #aprato's solution but found a few corner cases where the new layers from new VCs (eg. UINavigationItemButtonViews, UINavigationItemViews, etc) would be automatically inserted into a position below the extraColorLayer (which would cause those title or button elements to be affected by the extraColorLayer and thus fainter in color than they normally would be). So I adjusted #aprato's solution to force the extraColorLayer to stay at the index position 1. At index position 1, the extraColorLayer stays right above the _UINavigationBarBackground, but underneath everything else.
Here's my class implementation:
- (void)setBarTintColor:(UIColor *)barTintColor
{
[super setBarTintColor:barTintColor];
if (self.extraColorLayer == nil)
{
self.extraColorLayer = [CALayer layer];
self.extraColorLayer.opacity = kDefaultColorLayerOpacity;
[self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear
}
self.extraColorLayer.backgroundColor = barTintColor.CGColor;
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.extraColorLayer != nil)
{
self.extraColorLayer.frame = CGRectMake(0, 0 - kSpaceToCoverStatusBars, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + kSpaceToCoverStatusBars);
}
}
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview
{
[super insertSubview:view aboveSubview:siblingSubview];
[self.extraColorLayer removeFromSuperlayer];
[self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear
}
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index
{
[super insertSubview:view atIndex:index];
[self.extraColorLayer removeFromSuperlayer];
[self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear
}
- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview
{
[super insertSubview:view belowSubview:siblingSubview];
[self.extraColorLayer removeFromSuperlayer];
[self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear
}
I've improved your code in my fork: https://github.com/allenhsu/CRNavigationController
With my modification, the result color on screen (picked on white background) will be exactly the same value passed into setBarTintColor. I think it's an amazing solution.
None of these hacks are required :). Simply set:
self.navigationController.navigationBar.translucent = NO;
For iOS 7, the default translucency has been kept to TRUE.
On a related note, you can set your title text color (with shadow) easily via:
NSShadow *titleShadow = [[NSShadow alloc] init];
titleShadow.shadowOffset = CGSizeMake(0.0f, -1.0f);
titleShadow.shadowColor = [UIColor blackColor];
NSDictionary *navbarTitleTextAttributes = #{NSForegroundColorAttributeName: [UIColor whiteColor],
NSShadowAttributeName: titleShadow};
[[UINavigationBar appearance] setTitleTextAttributes:navbarTitleTextAttributes];
I came across this Q/A while trying to setup an uniformly colored navigation bar with transparency DISABLED on iOS 7.
After experimenting a while with barTintColor I figured out that a very easy way of having an opaque navigation bar is to make a single pixel image of the desired color, make a stretchable image out of it, and setting it to the backgroundImage of the navigation bar.
UIImage *singlePixelImage = [UIImage imageNamed:#"singlePixel.png"];
UIImage *resizableImage = [singlePixelImage resizableImageWithCapInsets:UIEdgeInsetsZero];
[navigationBar setBackgroundImage:resizableImage forBarMetrics:UIBarMetricsDefault];
Three lines of code, very simple and works BOTH on iOS 6 and iOS 7 (barTintColor is unsupported on iOS 6).
Theres a great Dropin UINavigationController replacement available from Simon Booth available at GitHub Here GitHub - C360NavigationBar
If you're backward supporting iOS6 do a check on the root view controller as such:
PatientListTableViewController *frontViewController = [[PatientListTableViewController alloc] init];
UINavigationController *navViewController = [[UINavigationController alloc] initWithNavigationBarClass:[C360NavigationBar class] toolbarClass:nil];
if ([navViewController.view respondsToSelector:#selector(setTintColor:)]) {
//iOS7
[navViewController.view setTintColor:self.navBarTintColor];
[[C360NavigationBar appearance] setItemTintColor:self.navBarItemTintColor];
} else {
//iOS6
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque animated:NO];
navViewController.navigationBar.tintColor = self.navBarTintColor;
}
[navViewController pushViewController:frontViewController animated:NO];
self.window.rootViewController = navViewController;
As #bernhard mentioned above it's possible to saturate the bar tint color to get desired navigation bar appearance.
I wrote an BarTintColorOptimizer utility for that kind of adjustment. It optimizes translucent bar tint color to make the bar's actual color match the desired color in iOS 7.x and later. Look at this answer for details.
Frankly speaking, above answers might be right but following trick worked for me with very ease.
// this is complete 100% transparent image
self.imageBlack = [[UIImage imageNamed:#"0102_BlackNavBG"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2)
resizingMode:UIImageResizingModeStretch];
// this is non-transparent but iOS7
// will by default make it transparent (if translucent is set to YES)
self.imageRed = [[UIImage imageNamed:#"0102_RedNavBG"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2)
resizingMode:UIImageResizingModeStretch];
// some navigation controller
[nvCtrLeft.navigationBar setBackgroundImage:self.imageRed
forBarMetrics:UIBarMetricsDefault];
// some another navigation controller
[nvCtrCenter.navigationBar setBackgroundImage:self.imageRed
forBarMetrics:UIBarMetricsDefault];
Here are the images used for self.imageRed and self.imageBlack.
< > black image is in this brackets won't be visible as it is transparent :)
< > red image is in this brackets.
is there a way to use #aprato solution without subclassing UINavigationBar.
In my project my main view is a UIViewController.
the problem is that the navigationController is a readonly property, is there a way to use you class with my project because i can't use : [[UINavigationController alloc] initWithNavigationBarClass:
thanks
An easy way to get the color you want is using
[<NAVIGATION_BAR> setBackgroundImage:<UIIMAGE> forBarPosition:<UIBARPOSITION> barMetrics:<UIBARMETRICS>];
As long as your image has some alpha, the translucency will work and you can set the alpha by changing the image. This was just added in iOS7. The width and height for the image are 640x88px for vertical (add 20 to the 88 if you want it to be underneath the status bar).

Resources