Daniel Chepenko
Daniel Chepenko

Reputation: 2268

How to add subview out of the parent view bounds

I have a view with bezier path mask layer. Now I need to add to my view a subview, that has to be placed outside of the superView

class MiddleSegmentView: UIView {

override func layoutSubviews() {
    super.layoutSubviews()
    createMaskView()
} 

func createPath() -> UIBezierPath {
    let width = self.frame.size.width
    let height = self.frame.size.height
    let lineWidth = -height/tan(angle)

    let path = UIBezierPath()
    path.moveToPoint(CGPoint(x: 0.0, y: 0.0))
    path.addLineToPoint(CGPoint(x: width, y: 0))
    path.addLineToPoint(CGPoint(x: width - lineWidth, y: height))
    path.addLineToPoint(CGPoint(x: lineWidth, y: height))
    path.addLineToPoint(CGPoint(x: 0.0, y: 0.0))
    path.closePath()

    return path
}

func createMaskView() {
    let mask = CAShapeLayer()
    mask.path = createPath().CGPath
    self.layer.mask = mask
}

  //Creating shadow

  override func drawRect(rect: CGRect) {
    super.drawRect(rect)
    //createShadow()
    let shadowView = SegmentShadow(frame: CGRect(x: 0, y: 0, width: 10, height: 40))
    self.clipsToBounds = false
    self.addSubview(shadowView)
    shadowView.bringToFront()
}

}

Although my view is generating correctly my shadowView is being cropped by superView bounds. How can I avoid such behaviour?

enter image description here

Upvotes: 1

Views: 657

Answers (1)

Johnny Rockex
Johnny Rockex

Reputation: 4196

There are a couple of ways to achieve what you want:

METHOD ONE: INSERT SHADOW SHAPE INTO EACH TAB

enter image description here

Create the tabs you want and then add a custom CAShapeLayer to each one:

//insert a CAShapeLayer into each 'tab'
CAShapeLayer * shadowLayer = [self trapezium];
shadowLayer.fillColor = grey.CGColor;
shadowLayer.shadowOpacity = 1.0f;
shadowLayer.shadowRadius = 1.0f;
shadowLayer.shadowOffset = CGSizeZero;
shadowLayer.shadowColor = [UIColor blackColor].CGColor;
[tab.layer insertSublayer:shadowLayer atIndex:0];

You can access the tabs and shadowLayer like this (given an array 'tabs'):

//then access like this
UIView * tab = tabs[2];
[self.view bringSubviewToFront:tab];

CAShapeLayer * layer = (CAShapeLayer*)tab.layer.sublayers[0];
layer.fillColor = orange.CGColor;

METHOD TWO: MOVE SHADOW VIEW

enter image description here

Another way is to create a single custom 'tabShadowView' and simply move that to the location of the selected tab. Here's the code for laying out the tabs in a custom view:

grey = [_peacock colourForHex:@"#ACA499" andAlpha:1.0f];
orange = [_peacock colourForHex:@"#CB9652" andAlpha:1.0f];

tabShadowView = [UIView new];
tabShadowView.frame = CGRectMake(0, 20, 100, 40);
tabShadowView.layer.shadowPath = [self trapezium].path;
tabShadowView.layer.shadowColor = [UIColor blackColor].CGColor;
tabShadowView.layer.shadowOffset = CGSizeZero;
tabShadowView.layer.shadowRadius = 1.0f;
tabShadowView.layer.shadowOpacity = 0.5f;
[self.view addSubview:tabShadowView];

customTabView = [UIView new];
customTabView.frame = CGRectMake(0, 0, w, 60);
[self.view addSubview:customTabView];

NSArray * titles = @[@"Button A", @"Button B", @"Button C",@"Button D"];
tabs = [NSMutableArray new];

float xOff = 0.0f;
for (NSString * title in titles){

    UIView * tab = [UIView new];
    tab.frame = CGRectMake(xOff, 20, 100, 40);
    tab.backgroundColor = grey;
    tab.layer.mask = [self trapezium];
    [customTabView addSubview:tab];
    [tabs addObject:tab];

    UIButton * button = [UIButton new];
    button.frame = tab.bounds;
    [button setTitle:title forState:UIControlStateNormal];
    [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    button.titleLabel.textAlignment = NSTextAlignmentCenter;
    button.titleLabel.font = [UIFont systemFontOfSize:14.0f weight:UIFontWeightRegular];
    [button addTarget:self action:@selector(tabSelected:) forControlEvents:UIControlEventTouchUpInside];
    [button setTag:[titles indexOfObject:title]];
    [tab addSubview:button];

    xOff += 70.0f;
}

[self updateTabsForSelected:tabs[1]];

Put the above code in the place you layout your views. The tabShadowView is the view that we move about, it's right at the bottom of the view hierarchy. Then the for loop just adds tabs in on top of it. Here are the methods that it uses:

-(void)tabSelected:(UIButton *)button {

    UIView * tab = tabs[(int)button.tag];
    [self updateTabsForSelected:tab];
}
-(void)updateTabsForSelected:(UIView *)tab{

    for (UIView * view in customTabView.subviews){

        [customTabView sendSubviewToBack:view];
        view.backgroundColor = grey;
    }

    [customTabView bringSubviewToFront:tab];
    tab.backgroundColor = orange;
    tabShadowView.transform = CGAffineTransformMakeTranslation([tabs indexOfObject:tab]*70, 0);
}
-(CAShapeLayer *)trapezium {

    UIBezierPath * path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointZero];
    [path addLineToPoint:CGPointMake(20, 40)];
    [path addLineToPoint:CGPointMake(80, 40)];
    [path addLineToPoint:CGPointMake(100, 0)];
    [path closePath];

    CAShapeLayer * layer = [CAShapeLayer layer];
    layer.path = path.CGPath;

    return layer;
}

Tapping on a button then moves the shadow under the selected tab. The code is quick and dirty, you'll need to add more logic, clean up etc, but you get the point, method one inserts a shadow view into each, method two moves a single shadow view about underneath the tabs.

Upvotes: 2

Related Questions