Uiview Drawrect: Draw the Inverted Pixels, Make a Hole, a Window, Negative Space

UIView drawRect: Draw the inverted pixels, make a hole, a window, negative space

Add multiple subpaths to your context, and draw with mode kCGPathEOFill. The Quartz 2D Programming Guide explains in more detail.

// Outer subpath: the whole rect
CGContextAddRect(context, rrect);

// Inner subpath: the area inside the whole rect
CGContextMoveToPoint(context, minx, midy);
...
// Close the inner subpath
CGContextClosePath(context);

// Fill the path
CGContextDrawPath(context, kCGPathEOFill);

How to make a transparent hole in UIVisualEffectView?

Well I tested your code and the original code with makeClearHole function in the extension works fine! The problem lies somewhere else.

1- Change the CoverView as following*

class CoverView: UIView {
private lazy var blurView: UIVisualEffectView = {
let bv = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
bv.frame = bounds
return bv
}()

override init(frame: CGRect) {
super.init(frame: frame)
setup()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - Internal

func setup() {
addSubview(blurView)
blurView.snp.makeConstraints { maker in
maker.edges.equalToSuperview()
}
blurView.makeClearHole(rect: CGRect(x: 100, y: 100, width: 100, height: 230))
}
}

2- Give a frame to coverView in your view controller
The view controller you have is different. But you should give the CoverView instance a frame. This is how: (again, this is how I tested but your view controller is definitely different)

class ViewController: UIViewController {

var label: UILabel!
var coverView: CoverView!

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.

label = UILabel()
label.text = "HELLO WORLD"
label.font = UIFont.systemFont(ofSize: 40, weight: .black)


coverView = CoverView(frame: CGRect(x: 20, y: 200, width: 300, height: 400))

view.addSubview(label)
view.addSubview(coverView)

label.snp.makeConstraints { make in
make.center.equalTo(view)
}

coverView.snp.makeConstraints { make in
make.width.equalTo(coverView.bounds.width)
make.height.equalTo(coverView.bounds.height)
make.leading.equalTo(view).offset(coverView.frame.minX)
make.top.equalTo(view).offset(coverView.frame.minY)
}
}

}

** Result**

Transparent rect inside blur view

How to invert colors of a specific area of a UIView?

If you know how to code a CIColorKernel, you'll have what you need.

  • Core Image has several blur filters, all of which use the GPU, which will give you the performance you need.

  • The CIAreaAverage will give you the average color for a specified rectangular area.

Core Image Filters

Here is about the simplest CIColorKernel you can write. It swaps the red and green value for every pixel in an image (note the "grba" instead of "rgba"):

kernel vec4 swapRedAndGreenAmount(__sample s) {
return s.grba;
}

To put this into a CIColorKernel, just use this line of code:

let swapKernel = CIKernel(string:
"kernel vec4 swapRedAndGreenAmount(__sample s) {" +
"return s.grba;" +
"}"

@tww003 has good code to convert a view's layer into a UIImage. Assuming you call your image myUiImage, to execute this swapKernel, you can:

let myInputCi = CIImage(image: myUiImage)
let myOutputCi = swapKernel.apply(withExtent: myInputCi, arguments: myInputCi)
Let myNewImage = UIImage(ciImage: myOutputCi)

That's about it. You can do alot more (including using CoreGraphics, etc.) but this is a good start.

One last note, you can chain individual filters (including hand-written color, warp, and general kernels). If you want, you can chain your color average over the underlying view with a blur and do whatever kind of inversion you wish as a single filter/effect.

iPhone - Draw transparent rectangle on UIView to reveal view beneath

You have to override the top view's drawRect method. So, for example, you might create a HoleyView class that derives from UIView (you can do that by adding a new file to your project, selecting Objective-C subclass, and setting "Subclass of" to UIView). In HoleyView, drawRect would look something like this:

- (void)drawRect:(CGRect)rect {
// Start by filling the area with the blue color
[[UIColor blueColor] setFill];
UIRectFill( rect );

// Assume that there's an ivar somewhere called holeRect of type CGRect
// We could just fill holeRect, but it's more efficient to only fill the
// area we're being asked to draw.
CGRect holeRectIntersection = CGRectIntersection( holeRect, rect );

[[UIColor clearColor] setFill];
UIRectFill( holeRectIntersection );
}

If you're using Interface builder, make sure to change the holey view's class to HoleyView. You can do that by selecting in the view in Interface Builder and selecting the "Identity" pane in the inspector (its the one on the far right the the "i" icon).

You also have to set the top view to be non-opaque either with the following code snippet, or by un-checking the Opaque checkbox in the view's properties in Interface Builder (you'll find it in the View section of the view's attributes) and set its background color's opacity to 0% (background color is set in the same section).

topView.opaque = NO;
topView.backgroundColor = [UIColor clearColor];

If you want to do circles, you have to use Core Graphics (aka Quartz 2D). You'll probably want to read the programming guide, which is available here.

To draw an ellipse instead of the rectangle, your drawRect would look something like this:

- (void)drawRect:(CGRect)rect {
// Get the current graphics context
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetFillColorWithColor( context, [UIColor blueColor].CGColor );
CGContextFillRect( context, rect );

if( CGRectIntersectsRect( holeRect, rect ) )
{
CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor );
CGContextFillEllipseInRect( context, holeRect );
}
}

UIView with transparent in middle

From Duncan C, I get to know where should I start, then I found CALayer with transparent hole in it.

UIBezierPath *overlayPath = [UIBezierPath bezierPathWithRect:self.view.bounds];
UIBezierPath *transparentPath = [UIBezierPath bezierPathWithRect:CGRectMake(60, 120, 200, 200)];
[overlayPath appendPath:transparentPath];
[overlayPath setUsesEvenOddFillRule:YES];

CAShapeLayer *fillLayer = [CAShapeLayer layer];
fillLayer.path = overlayPath.CGPath;
fillLayer.fillRule = kCAFillRuleEvenOdd;
fillLayer.fillColor = [UIColor colorWithRed:255/255.0 green:20/255.0 blue:147/255.0 alpha:1].CGColor;

[self.view.layer addSublayer:fillLayer];

Make use of 2 UIBezierPath, then fill with color that I want (in my question is pink color), then add as sublayer

CALayer with transparent hole in it

I was able to solve this with Jon Steinmetz suggestion. If any one cares, here's the final solution:

int radius = myRect.size.width;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.mapView.bounds.size.width, self.mapView.bounds.size.height) cornerRadius:0];
UIBezierPath *circlePath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius) cornerRadius:radius];
[path appendPath:circlePath];
[path setUsesEvenOddFillRule:YES];

CAShapeLayer *fillLayer = [CAShapeLayer layer];
fillLayer.path = path.CGPath;
fillLayer.fillRule = kCAFillRuleEvenOdd;
fillLayer.fillColor = [UIColor grayColor].CGColor;
fillLayer.opacity = 0.5;
[view.layer addSublayer:fillLayer];

Swift 3.x:

let radius = myRect.size.width
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: self.mapView.bounds.size.width, height: self.mapView.bounds.size.height), cornerRadius: 0)
let circlePath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 2 * radius, height: 2 * radius), cornerRadius: radius)
path.append(circlePath)
path.usesEvenOddFillRule = true

let fillLayer = CAShapeLayer()
fillLayer.path = path.cgPath
fillLayer.fillRule = kCAFillRuleEvenOdd
fillLayer.fillColor = Color.background.cgColor
fillLayer.opacity = 0.5
view.layer.addSublayer(fillLayer)

Swift 4.2 & 5:

let radius: CGFloat = myRect.size.width
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: self.view.bounds.size.height), cornerRadius: 0)
let circlePath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 2 * radius, height: 2 * radius), cornerRadius: radius)
path.append(circlePath)
path.usesEvenOddFillRule = true

let fillLayer = CAShapeLayer()
fillLayer.path = path.cgPath
fillLayer.fillRule = .evenOdd
fillLayer.fillColor = view.backgroundColor?.cgColor
fillLayer.opacity = 0.5
view.layer.addSublayer(fillLayer)

How do I best silence a warning about unused variables?

You can put it in "(void)var;" expression (does nothing) so that a compiler sees it is used. This is portable between compilers.

E.g.

void foo(int param1, int param2)
{
(void)param2;
bar(param1);
}

Or,

#define UNUSED(expr) do { (void)(expr); } while (0)
...

void foo(int param1, int param2)
{
UNUSED(param2);
bar(param1);
}


Related Topics



Leave a reply



Submit