XCode iOS custom view color - ios

I've a UIViewController and in the main root view I've created a custom class and in drawRect I've created a layer with gradient color and added to it:
[self.layer insertSublayer:bgLayer atIndex:0];
I want to use this view to have the same background color to all my view.
Now, i've (in storyboard) added 5 labels and grouped together in a view (Editor -> Embedded In -> View).
So now I've created another subclass of UIView and in draw rect I have:
[self setBackgroundColor:[UIColor redColor]];
(for now I have simple red color, later I want to had shadow, border ecc)
Because I want that this subview (with the 5 labels) is red (sure, the background must remain gradient).
When I run the application I see the background correctly but the view that I've set the color red is display totally black.
If in storyboard I change the background to Clear Color (in the view that should be red), the 5 labels come in and the background is the same of the main view.
I've placed a breakpoint to drawRect of the "red view" and this is called but nothing happen.

I'd suggest you take a look at Appearance Proxies. You can do what you're looking to do more easily with them.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
[MyCustomView appearance].backgroundColor = [UIColor clearColor];
...
}
Also, you likely shouldn't be adding sublayers in drawRect, especially if your views don't move or resize. I typically just add them in -(void)awakeFromNib.
Edit:
To address your question directly, I find the best way to create a gradient background in a custom view is to override +(Class)layerClass to provide a gradient layer. What this will do is make the fundamental layer for your custom view be whatever layer class you return. Typically this may be a shape layer for instance (for a solid color). In our case, we want a CAGradientLayer. Then in -(void)awakeFromNib you can set your gradient colors without mucking about with sublayers.
What I'm guessing is your original issue is that you're adding a gradient sublayer without setting start and end colors which will draw on top of your background color as black.
Example:
#implementation MyCustomView
+ (Class)layerClass {
return [CAGradientLayer class];
}
- (void)awakeFromNib {
CAGradientLayer* gradientLayer = (CAGradientLayer*)self.layer;
gradientLayer.locations = #[#0, #1];
gradientLayer.colors = #[(id)[UIColor redColor].CGColor, (id)[UIColor greenColor].CGColor];
}
Let me know if you need more details or this isn't working for you.

Related

Do I really need drawRect() in custom UIView?

I am new to iOS and trying to understand the use for drawRect() in custom UIViews, so I have simple custom view which I initialize from code.I want to update its colors for instance and I see two approaches as shown below. Which one should I use and why?
//VController
CustomView *cv = [[CustomView alloc] initWithFrame:...]
...
[cv updateColors];
//CustomView
-(id) initWithFrame {}
-(id) initWithCoder {}
-(void) updateColors(UIColor *color){ ----(1)
...Draw here with new color ...
view1.backgroundColor = color;
view2.backgroundColor = color;
}
- (void) drawRect{
... draw here with new color ... ---------(2)
view1.backgroundColor = color;
view2.backgroundColor = color;
}
If all you want to do is change the background color of this view or some of its subviews, you absolutely should not misuse drawRect: for this. drawRect: is for when you want to draw the view (i.e. its content) when the system believes its needs refreshing; it is called at many and unpredictable times, and you don't need that - you just need to change the background color, a feature of the view, on demand. Similarly drawRect: is not the place to perform management of subviews.
But if you are going to draw the view's content (e.g. the view displays a circle and you need to draw that circle to portray the view) then you must use drawRect: for that; it is the only place where the view gets a chance to draw itself.

Why can't you change a UIView background color in drawRect?

In theory you can easily set the background color via:
self.backgroundColor = [UIColor redColor];
in drawRect, but this does does not have any effect. You can change the view's size, its borders, its subviews, etc., but not background Color. Similar SO queries suggest to add a subView or to change the backgroundColor in the initWithFrame or initWithCoder initialization messages. My question is why does it not work in drawRect when other characteristics can be changed?
Changing the view's layer backgroundColor also does nothing, perhaps for the same reason. What is the relationship between the view's backgroundColor and its layer's backgroundColor, since they are not the same?
If you set backgroundColor when the view is first configured (i.e., before the OS calls drawRect), that backgroundColor will be used without any further code on your part. As the UIView Class Reference says, the backgroundColor property is used to "set the view’s color rather than drawing that color yourself."
But if you attempt to set it in drawRect, the background fill has already been performed, so you won't immediately see the effect of changing the background color (unless you manually perform another fill yourself in drawRect). The drawRect method should be used for rendering the view, not for configuring it.
If the UIView sets the backgroundColor as the view is being configured (e.g. in whichever init method you avail yourself of), that background color will used, without any need to do anything else in drawRect.
As Rob stated in his answer, the background has already been drawn when the drawRect method is called. If you need to change the background color at this point, you will have to draw it yourself:
[self.backgroundColor setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
So you still can change the value of the property, but it will not be used until the view is drawn again.
Update for swift3 on drawRect function :
self.backgroundColor = UIColor.red
self.backgroundColor?.setFill()
UIGraphicsGetCurrentContext()!.fill(rect);
Set your background color into your awakeFromNib function:
// Objective-C
- (void)awakeFromNib {
self.backgroundColor = [UIColor redColor];
}
// Swift
override func awakeFromNib() {
backgroundColor = UIColor.red
}

PageControl backgroundColor when used in UIPageViewController

I'm trying to implement a simple tutorial using UIPageViewController in iOS7. I implement all methods in UIPageViewControllerDataSource and it gives me a UIPageControl. Everything works except that the pageControl's background is not transparent so it blocks part of the other views behind it.
I try to change the pageControl's appearance with the following code
pageControl.backgroundColor = [UIColor greenColor]; //works, background is green
pageControl.backgroundColor = [UIColor clearColor]; //not working, background is black.
//updates
It turns out that the black color is the background color of the PageControl's layer. And this Page Control layer is not on top of my own ViewControllers that I used for pages. They are in parallel.So no matter how I modify its colors, I cannot get my ViewControllers to take the full screen.
Is there an easy way to move the PageControl on top of my ViewControllers?
It could be the background of the parent view. I had a similar situation where the background color was actually the background of my parent control, not my own. Here is a tool I use that helped me narrow this down : http://www.sparkinspector.com. Here is another tool that does the same thing : http://revealapp.com.
Also one more thing - not sure if this would apply to your case, but it could be the background layer that might need its color set
CALayer *layer = self.layer;
[layer setBackgroundColor:[UIColor clearColor]];
if you set something to be clear color, the background color then depends on whatever is on the background, the view the page control is in (usually a uipagecontrol) has no background, and this is why you're seeing it as black.

iOS - Button background changes with gradient background on iPad

I have one view and on view several buttons. I have added a gradient background to view and background image to buttons. But my button's color is changing as gradient background goes down on iPad 6.0.
I have tried to add gradient background to view using both programatically and by adding a gradient image to view by:
CAGradientLayer *bgLayer = [BackgroundLayer greyGradient];
bgLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:bgLayer atIndex:0];
And
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"keyboard_BG.png"]];
But nothing is working.
But my button's color is changing as gradient background goes down on iPad 6.0.
It sounds like the background image you're using for the buttons is semi-tranparent. Try removing the image, and instead setting an opaque background color. If that works, you'll probably need to edit the background image to make it fully opaque.

Blacking out areas of a UIImageView or showing part of the view

Is there a way to black out a region (defined by a frame) of a UIImageView without creating an overlaying UIView instance (with this frame) and blacking that out?
Is there similarly a way to reveal part of a UIImageView without using UIView instances to black out the rest of the image?
I am going to cheat a bit here and suggest that you use layer as they are light weight.
CALayer *blackLayer = [CALayer layer];
blackLayer.backgroundColor = [UIColor blackColor];
blackLayer.frame = imageView.bounds;
[imageView.layer addSublayer:blackLayer];
For the second part you can consider using a grid of layers (black). When user touches the image view, you can pick the layers from the area he has touched and remove them from the super layer i.e. the image view's root layer.
you can subclass the UIIMageView and draw as per your needs in the drawRectMethod.
You can create a subclass of UIIMageView and have your definition of drawRectMethod over there.
You can also invoke the drawRectMethod at your will by calling setNeedsLayout method on the view .( setNeedsDisplay - causes drawRect to be called - . Only contentMode = UIViewContentModeRedraw will cause setNeedsDisplay to be called when a view's bounds changes).
UPDATE:
http://developer.apple.com/library/ios/#documentation/CoreGraphics/Reference/CoreGraphics_Framework/_index.html

Resources