Can you override a parent UIView's alpha value on one of its subviews? - cocoa-touch

I have a somewhat transparent view (alpha = 0.6) that has some subviews. I'd like one of the subviews (a UILabel) to be drawn with alpha of 1.0 because the blending makes the text difficult to read but by adding it as a subview of the main view it has adopted its parent's alpha value. Is there a way to override this behavior? I believe I'll have to remove it from the subview but wanted to ask and see if maybe I'm missing something.

Brian is right.
For example :
view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.6];
will make a Black background in alpha 0.6 and other views won't be affected.

Another approach which worked easily is to not modify the alpha channel on the UIView. Instead, modify the alpha layer on the backgroundColor object of the UIView.
Of course this depends on why you made the alpha layer transparent, but it worked well for my requirements.

You are correct. You'll have to move the UILabel out of the transparent view to get it to render as non-transparent.

Related

a UIView scaled with animateWithDuration doesn't scale its subviews

I have a UIView that contains two UIImageViews.
when I perform animateWithDuration and change its frame (x, y, width, height) , it's subviews aren't resized according to its frame.
How can this be done? is it something with contentMode of the superview or subviews?
You need to configure the subviews auto-resizing rules. If you don't specify any rules the subviews will maintain their size and position (relative to the top left corner).
Check the auto-resizing documentation here. That's the 'simple' version which works from before iOS 6. From iOS 6 onwards you can also use auto-layout which is described here.

Fade out UIView?

I have an UIView here that I'd like to partially fade the bottom of. Basically I want the alpha value to decrease the further down on the UIView we go. So the top of the UIView is completely visible, while the bottom part has an alpha value of 0.
Anybody got any suggestion as to how to accomplish this?
CALayer has a property called mask, which is designed for doing precisely this sort of effect. If you assign another CALayer to the mask (one that has no superlayer), the alpha channel of that second CALayer is used when compositing the first CALayer.
Be careful, though, as using the mask property has a significant performance hit.

How should I wrap a custom NSView in an NSScrollView?

Cocoa's NSScrollView is horribly under-explained. I hope someone here knows what it's all about and can spare me a few seconds.
So I have a custom NSView. I implement -drawRect: for it to draw something, fill itself with colour, whatever. Then I have an NSScrollView wrapping it (set up through the interface Builder).
Now the inner, custom, view must have a size larger than that which fits in the outer scroll view—for it to scroll. That much I realise. I have incidentally configured it so that the scroll view adjusts to the surrounding window’s size, but that shouldn’t matter.
I override my inner view’s -frame method to return a frame sized at least 1000x1000.
- (NSRect)frame {
CGFloat w = 1000;
CGFloat h = 1000;
if (self.superview.bounds.size.width > w)
w = self.superview.bounds.size.width;
if (self.superview.bounds.size.height > h)
h = self.superview.bounds.size.height;
return NSMakeRect(0, 0, w, h);
}
Here’s the outcome, which I have trouble interpreting:
I can scroll when the scroll view encloses an area smaller than 1000x1000
BUT
The only area filled with colour (i.e. that my -drawRect: method has any effect on) is
as large as the scroll view’s bounds
located at (0,0. I use flipped, so that’s top left, and it ends up being outside the visible area after scrolling.
The visible area that lies outside this irrelevant rectangle is not painted at all.
I don’t know anything beyond this point. It seems like the rect for drawing is clipped to the scroll view’s position in the window, and size, or something—but it does not take the scrolled "location" into account.
It should be noted that I don't really expect anything else to happen. I feel I am missing a piece, but can't find which. Sorry for the wall of text, but I can’t explain better right now. I hope it is easier to answer than it is to ask.
Regards and hope,
Not Rick Astley
It's a very very very bad idea to overwrite -frame. There is so much that depends on the actual instance variable having a correct value. Instead try to set the frame to the one you want using setFrame:, that might fix all your problems if you're lucky...
I agree with Max's warning that you shouldn't override -frame. If you want to constrain the set frame, override its setter ( -setFrame: ) and the designated initializer ( -initWithFrame: ) and adjust the proposed frame as desired.
Regarding your overall problem, I wonder if your problem is conceptual. The argument for -drawRect: (the dirty rectangle you're asked to redraw) is useful if you're drawing something that you can redraw incrementally in parts (like a grid - any grid blocks intersecting dirtyRect can be redrawn and the rest can be ignored). If you're doing something that has to be completely redrawn, you should use [self bounds] and not the dirty rect passed at drawRect.
For example, if you have just a standard gradient background, it's difficult to tell from dirtyRect which part of the gradient to redraw and infinitely easier just to redraw the whole view with the gradient, ignoring dirtyRect altogether.
You're right in assuming that only the area of your view exposed by the scroll view's clip rect will normally be asked to redraw when scrolling. There're also interactions with the scroll view's -copiesOnScroll to consider.
I hope this helps.
Use of the NSScroller really relies on a solid understanding of the MVC paradigm. Apple's docs really focus on showing a photo and a set of text, but not much else. The use of NSScrollView is something that I've struggled with in the past.
First off, do not override frame. Use setFrame to tell the scrollView how large the working area is, and then just simply draw in the area the frame encompasses. As I understand it, a custom NSView and the encompassing NSScrollView takes care of the rest, such as what to draw where when. In other words, ignore the bounds of the rect passed into drawRect and instead draw within the bounds of the frame you sent to scrollView; don't worry about what is visible and what isn't because that is the job of the framework.
Here is where the MVC paradigm comes in: setFrame should be used when your Model is updated. So, if an object falls outside of the current bounds of the frame, then use setFrame to set the newly expanded bounds, and then draw within that area.

setOpaque:NO vs setBackgroundColor:[NSColor clearColor]

I'm going through a tutorial on drawing a custom [shaped] window with cocoa by subclassing NSWindow.
The tutorial states that in initializer developer should do the following:
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
So i'm wondering what is the differnce between these two messages and why are they needed both since their result is the same.
References: tutorial can be found here.
I guess that the first message is required because the drawing system needs to know whether it should bother updating views that lie behind yours. For example, if a window in another application (behind your window) updates (say text appears etc) the windowing system would not normally need to redraw it, but since your window is transparent it does in this case.
So i'm wondering what is the differnce between these two messages and why are they needed both since their result is the same.
They're not the same.
Look at the documentation for the opaque property: It's how you tell NSView that you're going to draw in your entire bounds, completely covering anything below your view.
If you don't cover the entire bounds, or you don't always draw at 100% opacity, then your view is not opaque, and you should leave that property set to NO.
If you set your view's background color to clearColor (which is simply a color with 0% opacity), and don't draw at 100% opacity over the entire background, then your view is not opaque.
On the other hand, it is possible to have clearColor as your background and then completely draw over it, in which case your view is opaque and should set itself as such.

Setting the background of NSBox to a gradient programmatically without subclassing

I want to set the background of an NSBox to be a gradient. In Interface Builder it is possible to set the background color of an NSBox to selectedMenuColor which is a gradient.
NSBox only has a setFillColor method so how is Interface Builder filling it with a gradient?
How do I programmatically fill an NSBox without subclassing it? It would be trivial to subclass NSBox but the workings of Interface Builder suggest there may be better solution.
selectedMenuColor is a "magic" color that is not displayed as a solid color. Many of these "magic" colors exist in the system.
I have used colorWithPatternImage: for this before. But note that the image you use as the pattern will get tiled, so you will probably have to resize the image to the size of the box.
Probably the closest you could come would be to use an NSColor created with colorWithPatternImage:, then create the gradient you want as an image and load that in. Ugly, but should work. I think subclassing is your best bet.
The selectedMenuColor color is actually a pre-rendered image of a gradient, and not a gradient drawn on the fly, so there is not any way to specify an arbitrary gradient as a background color. Like Ben said, subclassing is probably the way to go.
In xib, select NSBox, then goto effect inspector, check NSBox for Core Animation Layer.
Now
IBOutlet NSBox *box;
[box.setWantsLayer:YES];
[box.layer setBackgroundColor:[[NSColor whiteColor] CGColor]];
or
[box.setWantsLayer:YES];
[box.layer setBackgroundColor:[[NSColor colorWithPatternImage:[NSImage imageNamed:#"white.gif"]] CGColor]];