Fade out UIView? - objective-c

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.

Related

Circle shaped, dynamic view with AutoLayout

Before AutoLayout I could do a view cut to a circle shape with setting .layer.cornerRadius to half of the view's height.
Now, using AutoLayout how can I achieve, to my view keep look like a circle?
I have already tried and failed:
using KVO to find out when frame changes. It gets called, but at that point setting cornerRadius on the view does not have any effect
calling -setNeedsLayout to have frame values before I set cornerRadius (does not work either)
You need to set the property masksToBounds on the layer.
xyz.layer.masksToBounds = YES;
Further, the reason why this actually won't affect Auto Layout is because the view's frame will remain the same regardless of its corner radius.

Howto CALayer's image scale independently of CALayer's bounds

I can't find an answer for this one.
I would like to know how to have the image size in a calayer's to be lower than calayer's bound's size.
I've got several pawns in an iPad game, each is a CALayer and I have them resize simply with a contentsGravity=kCAGravityResizeAspect. Image is 128x128 inside of a CALayer of 30x30 so the image gets resized automatically to 30x30 and because of both being a box, aspect ratio maintains and works.
Here I set CALayer's bounds proportional relative to superview's size, so the Pawns always present the same relative size to the view. This one is inside my sprite class subclass of calayer:
-(void) setSpriteScaleToDice {
CGFloat newSize = [self superlayer].bounds.size.width * 0.066666667f;
self.bounds=CGRectMake(0.0f, 0.0f, newSize, newSize);
self.contentsGravity = kCAGravityResizeAspect;
}
Note that in my case the CALayer bounds gets a maximum of 30x30 which is small for a touch. That's the problem I'm facing, due to this small size it's difficult to "touch" them, sometimes touch fails...
One of the ideas that I'm thinking is to increase the "bounds" of the calayer, while keeping the image at its original size. The problem is that I've search a lot and tried several options with contentsGravity, contentsCenter, contentsScale, etc... without success.
In particular, as per apple docs looks like the way to go is with contentsCenter (and not using contentsGravity), however I get deformation in the bitmap...
Please, any idea is really welcome, and thanks in advance,
Luis
This is probably a silly question, but why are you using CALayers for this instead of UIViews? UIImageView has a contentMode property that lets you do this easily (not to mention being easier to use for touch event handling).
That said, CALayer has a contentsRect property that appears to let you define a sub-rectangle for contents to be drawn within, so that may let you do what you want.
Another option would be to place your image layer inside a larger layer and use that for the hit test.
CAlayer CGFloat contentsScale
/* Defines the scale factor applied to the contents of the layer. If
* the physical size of the contents is '(w, h)' then the logical size
* (i.e. for contentsGravity calculations) is defined as '(w /
* contentsScale, h / contentsScale)'. Applies to both images provided
* explicitly and content provided via -drawInContext: (i.e. if
* contentsScale is two -drawInContext: will draw into a buffer twice
* as large as the layer bounds). Defaults to one. Animatable. */
If you want your image drawn in the CALayer at a size other than the CALayer you need to create your own drawInContext: method and draw the image rather than setting the CALayer's contents property. Do not set the contents property, create your own to track the image you want to draw.

How do I restrict touches on a UIImageView to the opaque parts of its UIImage?

I have a UIImageView with userInteractionEnabled set to YES, and a few gesture recognizers on it which let the user interact with it. The view's image is of a shape, with the rest transparent, and I only want the opaque parts of the image to accept touches (the superview is also interactive).
I'm guessing I need to override a hit testing method, but which one and how?
You can obtain the the pixel color information on the touched point ( How to get the RGB values for a pixel on an image on the iphone ). May be you can test for its alpha value to determine whether it is opaque.
Although this isn't the exact answer you're after, this should give you an idea of one way of achieving this. This blog entry is using the Cocoas2D framework.
http://abitofcode.com/2011/07/irregular-touch-detection-when-cgrect-is-not-enough-part-1/

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.

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

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.