Monday, July 2, 2012

Rounded Corners With Ruby Motion



root_view_controller.rb
 

class RootViewController < UIViewController
  def viewDidLoad
    view.backgroundColor = UIColor.lightGrayColor 
@rounded_rect = UIView.alloc.initWithFrame([[view.size.width / 2 - 100, 
 view.size.height / 2 - 100], [200, 200]]) 
    @rounded_rect.backgroundColor = UIColor.greenColor
 
    view.addSubview @rounded_rect
  end
end


So you should have something that looks like this.

 

 

The Easy Way

Okay so real quick I thought I would show you the easy way and show you the problem I found with it. To quickly achieve this we just need to set the cornerRadius on the UIView’s layer. So after setting the backgroundColor for @rounded_rect I am going to add this line.


root_view_controller.rb
1
@rounded_rect.layer.cornerRadius = 30.0
Awesome! We now have some rounded corners!

The Problem

Okay so this works out great for us if we need to quickly add rounded corners to a view. But the problem comes when we add subviews that are going to be in the area of those rounded corners. To show you this I am going to add another UIView as a subview to our @rounded_rect view. We will call this @non_rounded_subview, I am going to make this subview the same size as its parent and color it red by adding the following.


root_view_controller.rb
1
2
3
4
@non_rounded_subview = UIView.alloc.initWithFrame(@rounded_rect.bounds)
@non_rounded_subview.backgroundColor = UIColor.redColor

@rounded_rect.addSubview @non_rounded_subview
OH NOES! Where did our rounded corners go?

Our rounded corners are still there, however the parent views rounded corners do not clip the child view. To show this better I will set the backgroundColor of @non_rounded_subview to have a slight transparent to better show this.


root_view_controller.rb
1
2
@non_rounded_subview.backgroundColor = UIColor.colorWithRed(1.0,
                                          green: 0.0, blue: 0.0, alpha: 0.5)

The Solution

So the easy way works great if you just need some simple rounded corners and arent going to be placing anything over them. However in my case I needed the rounded corners of the parent view to clip the child view. The solution I came up with ended up being even greater then I expected, not only cliping child views but giving me alot more control over the corners. To accomplish this I used UIBezierPath and CAShapeLayer to create a layer mask and then applied the layer mask to the parent view (@rounded_rect). To do this I removed the following line


root_view_controller.rb
1
@rounded_rect.layer.cornerRadius = 30.0
and replaced it with this


root_view_controller.rb
1
2
3
4
5
6
7
mask_path = UIBezierPath.bezierPathWithRoundedRect(@rounded_rect.bounds,
               byRoundingCorners: UIRectCornerAllCorners,
               cornerRadii:       CGSizeMake(30.0, 30.0))
mask_layer = CAShapeLayer.layer
mask_layer.frame = @rounded_rect.bounds
mask_layer.path = mask_path.CGPath
@rounded_rect.layer.mask = mask_layer
And our rounded corners are back and better than ever!!!

The cornerRadii for the UIBezierPath is where we set how much extreme we want our corners rounded. The first value of the CGSizeMake is a float for the width of your corner radius, the other being a float for the height of your corner radius. So you can change those values to something like


root_view_controller
1
cornerRadii:       CGSizeMake(40.0, 150.0))
And you will end up with something a little different.

You also have more control in which corners actually get the rounding by editing the “byRoundingCorners” value. I currently have that set to UIRectCornerAllCorners. Which we can set to UIRectCornerAllCorners, UIRectCornerTopLeft, UIRectCornerTopRight, UIRectCornerBottomLeft, UIRectCornerBottomRight or any combonation of these seperated by a pipe ( | ). For example to round only the top left and bottom right corners I would change the following.


root_view_controlelr.rb
1
byRoundingCorners: UIRectCornerAllCorners,
to


root_view_controlelr.rb
1
byRoundingCorners: UIRectCornerTopLeft | UIRectCornerBottomRight,
Ending up with,

Well thats about it, in the end my code ended up looking like this incase you missed something. Or you can check it out on my Github at https://github.com/bradylove/RoundedCorners-RubyMotion


root_view_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class RootViewController < UIViewController
  def viewDidLoad
    view.backgroundColor = UIColor.lightGrayColor

    @rounded_rect = UIView.alloc.initWithFrame(
      [[view.size.width / 2 - 100, view.size.height / 2 - 100], [200, 200]])
    @rounded_rect.backgroundColor = UIColor.greenColor

    mask_path = UIBezierPath.bezierPathWithRoundedRect(@rounded_rect.bounds,
                   byRoundingCorners: UIRectCornerTopLeft | UIRectCornerBottomRight,
                   cornerRadii:       CGSizeMake(40.0, 100.0))
    mask_layer = CAShapeLayer.layer
    mask_layer.frame = @rounded_rect.bounds
    mask_layer.path = mask_path.CGPath
    @rounded_rect.layer.mask = mask_layer

    @non_rounded_subview = UIView.alloc.initWithFrame(@rounded_rect.bounds)
    @non_rounded_subview.backgroundColor = UIColor.colorWithRed(1.0,
                                              green: 0.0, blue: 0.0, alpha: 0.5)

    @rounded_rect.addSubview @non_rounded_subview
    view.addSubview @rounded_rect
  end
end

No comments: