Can you limit UIView Clip Subviews to certain sides of a view?

7k views Asked by At

I have a UIScrollView that contains other subviews that are partially drawn outside of the scrollview. These views extend vertically above the scrollview. Is it possible to only allow the subviews to be drawn outside the top of the scrollview, and not allow them drawn outside of the left and right sides of the scrollview?

What is happening, is that when I manually scroll left and right, the subviews are being drawn outside of the scrollview because of the content size. Once the subview's are scroll outside of the scrollview's frame, I want to clip the subviews.

Any suggestions or is this possible?

4

There are 4 answers

0
Sébastien On

Here's how I implemented Max Chuquimia's nice solution in Objective-C:

CALayer* maskLayer = [CALayer layer];
maskLayer.backgroundColor = [UIColor blackColor].CGColor;
maskLayer.frame = CGRectMake(0,-100,
                            parentView.frame.size.width, parentView.frame.size.height + 100);
parentView.layer.mask = maskLayer;

Any part of the parent view or its children which is not covered by black pixels in the mask will be clipped.

In this snippet, we clip left and right, but leave 100 points on top and below the parent view for children to overflow. (the mask is bigger than the parent view)

0
Fattie On

Limit clip to certain sides of a view:

It's common to do this on table or collection views for example:

/*
This is a UICollectionView, which clips normally on the left and right,
but allows some extra space horizontally.
A typical example is you need to clip the scrolling items but you
still need to allow shadows.
*/

import Foundation
import UIKit

class CustomClipCollectionView: UICollectionView {
    
    private lazy var extraSpaceOnBaseButStillClipSidesNormally: CALayer = {
        let l = CALayer()
        l.backgroundColor = UIColor.black.cgColor
        return l
    }()
    
    override func layoutSubviews() {
        extraSpaceOnBaseButStillClipSidesNormally.frame = bounds.insetBy(
                          dx: 0, dy: -10)
        layer.mask = extraSpaceOnBaseButStillClipSidesNormally
        super.layoutSubviews()
    }
}

Note! You turn off the normal "clip to bounds" feature, when using this. The ".mask" system for clipping is different and separate from the "clip to bounds" system.

2
lxt On

You can't specify individual sides you'd like to clip, but you could basically fake this by placing a new UIView alongside the edge you wanted to clip (and so effectively clipping it). Alternatively you could think about ways to change your view hierarchy so that you don't have to clip subviews at all (perhaps by adjusting the scroll view's bounds and layout in some way).

3
Max Chuquimia On

I've managed to achieve this effect by using the layer's mask property and a CALayer. The following snippet clips only views from the top and the left side of a view:

let maskLayer = CALayer()
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.frame = CGRect(x: 0, y: 0, width: 2000, height: 2000)
aView.layer.mask = maskLayer

Note that I'm using the arbitrary number 2000 as the far right and lower bounds for clipping, so you'll need to adjust the number depending on how far you want your other views to clip.