Clipping a layer shadow to width and not height? - iphone

I'm adding a shadow to my view layer like so:
self.view.layer.shadowOffset = CGSizeZero;
self.view.layer.shadowOpacity = 0.10f;
self.view.layer.shadowRadius = 5.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
self.view.layer.shadowPath = [UIBezierPath bezierPathWithRect:
CGRectMake(self.view.bounds.origin.x, self.view.bounds.origin.y, self.view.bounds.size.width - 5.0, self.view.bounds.size.height)].CGPath;
self.view.clipsToBounds = NO;
What I want to do is somehow clip the shadow so that it does not extend beyond the width, but does extend beyond the height. Basically, I just want a 90 degree shadow rather than a shadow all around my bounds. I tried subtracting the shadowRadius amount from the bezierRect width, but this messes up the shadow flow on the bottom a little bit.
Any ideas how this can be accomplished?

You can add a new "container" view and add your view (the content view) as a subview. The container view should be higher than your view but the same width. If you set your container view to clip to its bounds it will clip the shadows on the side but allow shadows on the bottom and top.
_________
| _______ | <-- container
|| ||
|| || <-- your view (inside container)
||_______||
|`````````| <-- shadow of your view (inside container)
|_________|
In code this would look something like
// contentView is already created and configured...
UIView *containerView = [[UIView alloc] initWithFrame:
CGRectInset([contentView frame],
0, // keep the same width
-radius)]; // increase the height
[[self view] addSubview:containerView];
[contentView setCenter:CGPointMake(CGRectGetMidX([contentView bounds]),
CGRectGetMidY([contentView bounds]));
[containerView addSubview:contentView];
[containerView setClipsToBounds:YES];

Related

How can I set maskToBounds effect on CAShapeLayer(Rounded-Recr cornered UIView)?

I want to make the glow effect spread outside of thisView.
I used this code to make Half-Rounded rect cornered UIVew.
Round two corners in UIView
Here is my code.
+ (UIView *)makeUpperHalfRoundedView:(UIView *)view {
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = view.bounds;
UIBezierPath *roundedPath =
[UIBezierPath bezierPathWithRoundedRect:maskLayer.bounds
byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight
cornerRadii:CGSizeMake(8.f, 8.f)];
maskLayer.fillColor = [[UIColor blackColor] CGColor];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [roundedPath CGPath];
// maskLayer.masksToBounds = NO;
//Don't add masks to layers already in the hierarchy!
UIView *superview = [view superview];
[view removeFromSuperview];
view.layer.mask = maskLayer;
[superview addSubview:view];
return view;
}
And there are two image buttons in this view closed to the boundary.
If a touch event happen on buttons, glow effect shows(showsTouchWhenHighlighted)
Before using this piece of code, I just used
thisView.layer.cornerRadius = 8;
thisVIew.layer.masksToBounds = NO;
and glow effect spread outside of 'thisView'.
But after using CAShapeLayer in 'thisView' to masking half-roused rect, the glow effect are masked by boundary.
What you're seeing is exactly how a mask is supposed to work. You are applying the mask to the layer of the superview. Let's call that view superview (just as in your code). Okay, so what will that do? It will mask superview and all its sublayers. That includes its subviews. Its subviews are actually its sublayers.
So, since the buttons are subviews of superview, they are masked like everything else inside superview.
If you don't want that to happen, don't make the buttons subviews of superview. They can sit on top of superview without being its subviews, for example.

Draw horizontal and vertical lines in uiviewcontroller's view

I have a UIViewController with some images. I need to draw some horizontal and vertical lines in between images. Actually its like a hierarchy view. Is adding subviews with background color the best way to go?
You have 3 basic approaches:
Use QuartzCore and override drawRect: in a custom UIView subclass
Set the borderWidth and borderColor of the UIImageView layer property that contains each image
Create UIViews of height 1 for horizontal lines and width 1 for vertical lines, set the backgroundColor of the views and add them as subviews
3 is probably the easiest to implement, but not the most elegant, 1 is the most robust in terms of memory as you can also use drawInRect to draw your images into the same graphics context. This collapses the view hierarchy into a single view.
You can do it using layers as answered above or simply since you want just lines,use UIViews
Just like this
for(i=0;i<numberOfLine*heightofImage;i+=heightOfImage) {
UIView *horizontalLine=[[UIView alloc]initWithFrame:CGRectMake(x, i, height, 1)];
[self.view addSubView:horizontalLine];
}
Hope that helps
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *verticalLine = [[UIView alloc] initWithFrame:CGRectMake(roundf(self.view.bounds.size.width / 2), 0.0, 1.0, self.view.bounds.size.height)];
verticalLine.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.5];
verticalLine.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
[self.view addSubview:verticalLine];
UIView *horizontalLine = [[UIView alloc] initWithFrame:CGRectMake(0.0, roundf(self.view.bounds.size.height / 2), self.view.bounds.size.width, 1.0)];
horizontalLine.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.5];
horizontalLine.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.view addSubview:horizontalLine];
}
I used the 3rd option posted by Magic Bullet Dave and it is working great. Here's the code:
UIView *borderBottom = [[UIView alloc] initWithFrame:CGRectMake(0.0, 10, widthDesired, 1.0)];
borderBottom.backgroundColor = [UIColor grayColor];
[myView addSubview:borderBottom];
If you want to make a vertical line you'd simply use a width of 1 and then the height desired.

Set starting point of content size for UIScrollView

How do I set the starting point of a UIScrollView? I would like to add a UIImageView left of the UIScrollView but changing the contentSize only adds scrolling room to the right of the scrollview. How do I add an ImageView left of the scrollView's (0,0) point and make it part of the scrollview's content size?
Hopefully I've got what you're trying to do here. I think this just takes a few turns with the contentOffset to get right.
Starting off;
Add the scrollView at frame (0,0,320,480) - its a full screen scroller
set contentSize to (320*3, 480) - it now has a content with the width of 3 'pages'
Add your imageView as a subview to the scrollView at frame (320,0,320,480)
set contentOffset of the scrollView to (320, 0) - this will move the content of the scrollView left, in the negative x direction by 320
Now your imageView will be on screen, but it will have a 320px width both on the left and right on the scroller content.
(Note that in the code below, i've simply added a UIView and not an imageView)
UIScrollView *scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
scroller.delegate = self;
scroller.pagingEnabled = YES;
scroller.backgroundColor = [UIColor blueColor];
scroller.contentSize = CGSizeMake(960, 480);
UIView *imgView = [[UIView alloc] initWithFrame:CGRectMake(320, 0, 320, 480)];
[imgView setBackgroundColor:[UIColor redColor]];
[scroller addSubview:imgView];
[scroller setContentOffset:CGPointMake(320, 0)];
[self.view addSubview:scroller];
Does that help?
Actually the best solution to start in the middle of the scroller if you are in a iPhone you should
[scroller setContentOffset:CGPointMake(320, 0)];
[self.view addSubview:scroller];
And for iPad
[scroller setContentOffset:CGPointMake(1024, 0)];
[self.view addSubview:scroller];
Try setting the Content offset.
[scrollView setContentOffset:CGPointMake(320, 0.0)];
If you're simply looking to set where the scroller content should be positioned at the start, you could do so with the - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated method which will scroll the content to the point specified in the method.
Let me know if this is not what you wanted to know, and I'll get back to ya!
Cheers.
The key to setting the starting point, is to assign bounds with xy coordinates different than zero. Like this
lazy var contentView: UIView = {
let size = CGFloat(5000)
let view = UIView(frame: CGRectZero)
view.frame = CGRectMake(0, 0, size, size)
view.bounds = CGRectMake(-size/2, -size/2, size, size)
return view
}()
func centerContent() {
let frame = UIEdgeInsetsInsetRect(self.scrollView.bounds, self.scrollView.contentInset)
let navigationBarHeight = self.scrollView.contentInset.top
let x = (self.scrollView.contentSize.width/2) - (frame.size.width/2)
let y = (self.scrollView.contentSize.height/2) - (frame.size.height/2) - navigationBarHeight
self.scrollView.contentOffset = CGPointMake(x, y)
}

How to make UIScrollView respect the layout of the containing UIView?

I'm using a UIView to control the layout of my view (along with a view controller). I want UIScrollView to only use half of the vertical screen. That works fine if I use the upper half of the screen, but not the bottom half.
Here's the relevant code from the UIViewController:
- (void)loadView {
CGRect fullFrame = [[UIScreen mainScreen] applicationFrame];
//trying to put the scroll view on the bottom half of the screen, but does not work.
CGRect halfFrame = CGRectMake(0, fullFrame.size.height / 2 ,
fullFrame.size.width, fullFrame.size.height / 2);
//use this instead for the scroll view to go to the top half of the screen (and work properly)
//CGRect halfFrame = CGRectMake(0, 0 , fullFrame.size.width, fullFrame.size.height / 2);
UIScrollView* sv = [[UIScrollView alloc] initWithFrame:halfFrame];
[sv setContentSize:CGSizeMake(3 * halfFrame.size.width, halfFrame.size.height)];
CGRect stencilFrame = halfFrame;
UIView *leftView = [[UIView alloc] initWithFrame:stencilFrame];
stencilFrame.origin.x += stencilFrame.size.width;
UIView *centerView = [[UIView alloc] initWithFrame:stencilFrame];
stencilFrame.origin.x += stencilFrame.size.width;
UIView *rightView = [[UIView alloc] initWithFrame:stencilFrame];
//mix up the colors
[leftView setBackgroundColor:[UIColor redColor]];
[centerView setBackgroundColor:[UIColor greenColor]];
[rightView setBackgroundColor:[UIColor blueColor]];
//add them to the scroll view
[sv addSubview:leftView];
[sv addSubview:centerView];
[sv addSubview:rightView];
//turn on paging
[sv setPagingEnabled:YES];
UIView *containerView = [[UIView alloc]initWithFrame:fullFrame];
[containerView addSubview:sv];
[self setView:containerView];
}
Thank you in advance for any advice or help.
I figured it out. The crux of the problem is that views within the scroll view are initialized with the same frame as the scroll view itself. When the scrollView is initialized with halfFrame, the origin is (0, half the full screen size), which is ok since that is relative to the application window itself. However, the views that are put inside the scrollView (like leftView) are initialized to halfFrame, but in this case the origin is relative to the scrollView, effectively placing them off the screen. Setting the origin to (0,0) fixes this:
CGRect stencilFrame = CGRectMake(0, 0, fullFrame.size.width , fullFrame.size.height / 2);
contentSize must contain the rectangle of the view inside the scroll view. That is, the total size of all scrollable controls within. The frame of the UIScrollView decides how much scrolling is needed to let the user browse everything.
You don't have the "full frame" available if you have a nav bar or a tab bar. In general, code that uses [UIScreen mainScreen] for layout information is probably wrong.
Additionally, the status bar can change size if (for example) a call is in progress or tethering is enabled.
Instead, use any sane value for full frame and enable autoresizing:
CGRect fullFrame = {{0,0}, {320,480}};
...
sv.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
EDIT: You also probably need to subclass UIScrollView and implement -setFrame: so that it also sets the content size and -layoutSubviews to do the correct layout.

Need help about contentMode and autoresizingMask of UIImageView

i have picture like this image it will present on Landscape Mode
so if rotate device to Portrait, how to present image like this
Use an UIImageView with these settings:
imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight);
imageView.contentMode = UIViewContentModeScaleAspectFit;
It looks like what you're trying to do is clip the image on the right, while leaving the top, left, and bottom the same.
Two things to try:
1) Subclass UIView and in the drawRect method, adjust the rect and draw out the clipped image using CGContextDrawImage (context, rect, sourceImage.CGImage).
2) Use CALayers. You can adjust the 'contentsRect' of the imageView's layer:
CALayer* imageLayer = imageView.layer;
imageLayer.masksToBounds = YES;
CGRect rect;
if (inPortrait)
rect = CGRectMake(0.0, 0.0, 0.5, 1.0); // half size
else
rect = CGRectMake(0.0, 0.0, 1.0, 1.0); // full size
imageLayer.contentsRect = rect;
This slices the image in half along width. masksToBounds takes care of clipping sublayers (in case you have them). You can tweak the contentsRect unit rectangle to adjust where you want the cutoff. You may also want to adjust the imageView's own bounds as well to match the size.
This has an added bonus in that adjusting the contentsRect is automatically animated so when you do the rotation the width animates in and out nicely.
// Set the layer contents
view.layer.contents = [image CGImage];
// Stretch so that contents overflows the edges and keeps aspect ratio
view.layer.contentsGravity = kCAGravityResizeAspectFill;
// Clip off the overflow
view.layer.masksToBounds = YES;
// Set the frame
CGRect frame;
frame.origin = CGPointZero;
frame.size = view.superview.bounds.size;
view.frame = frame;
// Make sure it resizes when the superview does
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
You will have to work with view's layer.
You can find the appropriate guidelines at https://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/Layers.html
Hope this helps.
Thanks,
Jim.