Tại sao maskToBounds = YES lại ngăn chặn bóng CALayer?


83

Với đoạn mã sau, tôi đang thêm hiệu ứng đổ bóng vào một UIView của mình. Hoạt động khá tốt. Nhưng ngay sau khi tôi đặt thuộc tính maskToBounds của chế độ xem thành . Hiệu ứng đổ bóng không được hiển thị nữa.

self.myView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.myView.layer.shadowOpacity = 1.0;
self.myView.layer.shadowRadius = 10.0;
self.myView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.myView.layer.cornerRadius = 5.0;
self.myView.layer.masksToBounds = YES; // <-- This is causing the Drop shadow to not be rendered
UIBezierPath *path = [UIBezierPath bezierPathWithCurvedShadowForRect:self.myView.bounds];
self.myView.layer.shadowPath = path.CGPath;
self.myView.layer.shouldRasterize = YES;

Bạn có bất kỳ ý tưởng về điều này?

Câu trả lời:


166

Bởi vì bóng đổ là một hiệu ứng được thực hiện bên ngoài Chế độ xem, và maskToBounds được đặt thành CÓ sẽ nói với UIView không được vẽ bất kỳ thứ gì bên ngoài chính nó.

Nếu bạn muốn có chế độ xem RoundCorner có bóng, tôi khuyên bạn nên làm điều đó với 2 chế độ xem:

UIView *view1 = [[UIView alloc] init];
UIView *view2 = [[UIView alloc] init];

view1.layer.cornerRadius = 5.0;
view1.layer.masksToBounds = YES;
view2.layer.cornerRadius = 5.0;
view2.layer.shadowColor = [[UIColor blackColor] CGColor];
view2.layer.shadowOpacity = 1.0;
view2.layer.shadowRadius = 10.0;
view2.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
[view2 addSubview:view1];
[view1 release];

9
Chết tiệt! Điều đó có ý nghĩa!
jchatard

Cảm ơn bạn rất nhiều ... Nó đã giúp tôi rất nhiều. Biết ơn bạn .. :)
innodeasapps

1
Chỉ cần một lưu ý, hãy chắc chắn rằng các SuperView [View2 trong trường hợp này] có màu nền rõ ràng, đây là vấn đề của tôi anyway
Adam Carter

@GangstaGraham đảm bảo rằng bạn đang thêm view2 làm chế độ xem phụ của view1.
pxpgraphics

Chính xác những gì tôi cần!
Rohan Sanap

17

Bây giờ là iOS 6, mọi thứ có thể đã thay đổi. Câu trả lời của TheSquad không phù hợp với tôi cho đến khi tôi cố gắng thêm một dòng nữa view2.layer.masksToBounds = NO;, nếu không bóng tối sẽ không hiển thị. Mặc dù tài liệu nói masksToBoundslà KHÔNG theo mặc định, mã của tôi cho thấy điều ngược lại.

Đây là cách tôi tạo một nút góc tròn có bóng, một trong những đoạn mã được sử dụng phổ biến nhất trong ứng dụng của tôi.

button.layer.masksToBounds = YES;
button.layer.cornerRadius = 10.0f;

view.layer.masksToBounds = NO;      // critical to add this line
view.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 1.0f;
// set shadow path to prevent horrible performance
view.layer.shadowPath = 
    [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;      

[view addSubview:button];

BIÊN TẬP

Nếu các lượt xem cần được tạo hoạt ảnh hoặc cuộn, thì masksToBounds = YEShiệu suất đánh thuế đáng kể, có nghĩa là hoạt ảnh có thể sẽ bị nói lắp. Để có được góc tròn và bóng VÀ hoạt ảnh hoặc cuộn mượt mà, hãy sử dụng mã sau để thay thế:

button.backgroundColor = [UIColor clearColor];
button.layer.backgroundColor = [UIColor redColor].CGColor;
button.layer.masksToBounds = NO;
button.layer.cornerRadius = 10.0f;

view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
view.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
view.layer.shadowRadius = 2.0f;
view.layer.masksToBounds = NO;
view.layer.cornerRadius = 10.0f;  

[view addSubview:button];

4
+1 cho câu trả lời đầu tiên. Câu trả lời cập nhật của bạn sẽ không hoạt động đối với các góc tròn .. Nếu bạn đặt "maskToBounds" thành "KHÔNG" thì các góc tròn sẽ biến mất. Thay vì thay đổi điều đó, bạn có thể sử dụng thuộc tính "shouldRasterize" để có được hiệu suất tốt.
Dinesh Raja

Tính năng này hiện hoạt động vào năm 2020. Vì vậy, giải pháp đơn giản chỉ cần thêm một dòng.
GeneCode

4

Đây là phiên bản Swift 3 và IBDesignable của câu trả lời được đăng bởi @TheSquad.

Tôi đã sử dụng cùng một khái niệm trong khi thực hiện các thay đổi trong tệp bảng phân cảnh. Đầu tiên, tôi đã di chuyển targetView của mình (cái yêu cầu bán kính góc và bóng) bên trong một containerView mới . Sau đó, tôi đã thêm các dòng mã sau (Tham khảo: https://stackoverflow.com/a/35372901/419192 ) để thêm một số thuộc tính IBDesignable cho UIView Class:

@IBDesignable extension UIView {
/* The color of the shadow. Defaults to opaque black. Colors created
 * from patterns are currently NOT supported. Animatable. */
@IBInspectable var shadowColor: UIColor? {
    set {
        layer.shadowColor = newValue!.cgColor
    }
    get {
        if let color = layer.shadowColor {
            return UIColor(cgColor: color)
        }
        else {
            return nil
        }
    }
}

/* The opacity of the shadow. Defaults to 0. Specifying a value outside the
 * [0,1] range will give undefined results. Animatable. */
@IBInspectable var shadowOpacity: Float {
    set {
        layer.shadowOpacity = newValue
    }
    get {
        return layer.shadowOpacity
    }
}

/* The shadow offset. Defaults to (0, -3). Animatable. */
@IBInspectable var shadowOffset: CGPoint {
    set {
        layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
    }
    get {
        return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
    }
}

/* The blur radius used to create the shadow. Defaults to 3. Animatable. */
@IBInspectable var shadowRadius: CGFloat {
    set {
        layer.shadowRadius = newValue
    }
    get {
        return layer.shadowRadius
    }
}

/* The corner radius of the view. */
@IBInspectable var cornerRadius: CGFloat {
    set {
        layer.cornerRadius = newValue
    }
    get {
        return layer.cornerRadius
    }
}

Sau khi thêm mã này, tôi quay lại bảng phân cảnh và khi chọn Chế độ xem vùng chứa của mình, bây giờ tôi có thể tìm thấy một bộ thuộc tính mới trong trình kiểm tra thuộc tính:

nhập mô tả hình ảnh ở đây

Ngoài việc thêm các giá trị cho các thuộc tính này theo lựa chọn của tôi, tôi cũng thêm bán kính góc vào targetView của mình và đặt thuộc tính maskToBounds là true.

Tôi hi vọng cái này giúp được :)


4

Phiên bản Swift 3.0 với StoryBoard

Ý tưởng tương tự với @TheSquad. Tạo một dạng xem mới dưới dạng xem thực tế và thêm bóng vào dạng xem bên dưới.

1. Tạo chế độ xem dưới chế độ xem thực tế

Kéo một UIViewvào StoryBoard với cùng một ràng buộc với chế độ xem mục tiêu của bạn. Kiểm tra clip để ràng buộc cho chế độ xem mục tiêu. Đồng thời đảm bảo rằng chế độ xem mới được liệt kê trước chế độ xem mục tiêu để chế độ xem mục tiêu sẽ bao phủ chế độ xem mới.

nhập mô tả hình ảnh ở đây

2. Bây giờ liên kết chế độ xem mới với mã của bạn, thêm thêm bóng vào nó

Đây chỉ là một mẫu. Bạn có thể làm bất cứ điều gì bạn muốn ở đây

shadowView.layer.masksToBounds = false
shadowView.layer.shadowColor = UIColor.red.cgColor
shadowView.layer.shadowOpacity = 0.5
shadowView.layer.shadowOffset = CGSize(width: -1, height: 1)
shadowView.layer.shadowRadius = 3

shadowView.layer.shadowPath = UIBezierPath(rect: coverImage.bounds).cgPath
shadowView.layer.shouldRasterize = true

2

Tôi cũng gặp vấn đề về hiệu suất nghiêm trọng với bóng và các góc tròn. Thay vì sử dụng phần shadowPath, tôi đã sử dụng các dòng sau để giải quyết hoàn hảo cú đánh hiệu suất:

self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = UIScreen.mainScreen.scale;

Tuy nhiên, không thể tạo hoạt ảnh hoặc chuyển tiếp màn hình với điều này.
Van Du Tran

1

Đây là một trong những giải pháp:

     @IBOutlet private weak var blockView: UIView! {
         didSet {
          blockView.backgroundColor = UIColor.white
          blockView.layer.shadowColor = UIColor.black.cgColor
          blockView.layer.shadowOpacity = 0.5
          blockView.layer.shadowOffset = CGSize.zero

          blockView.layer.cornerRadius = 10
        }
      }
      @IBOutlet private weak var imageView: UIImageView! {
        didSet {
          imageView.layer.cornerRadius = 10
          imageView.layer.masksToBounds = true

          imageView.layer.shouldRasterize = true
        }
      }

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.