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**
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
Initializing Swift Properties That Require "Self" as an Argument
Ble Device Keep Connect with iPhone When Application Terminate
Add Custom Cordova Plugin to Ibm Worklight 6.1
Canopenurl Not Working in iOS 10
Memory Leak with "Libbacktracerecording.Dylib" in React Native iOS Application
Custom Uisearchbar with Uisearchcontroller
Re-Assigning Instance of Avaudioplayer in iOS13 Leads to Bad_Access Runtime Error
How to Emulate Nfc Cards in iOS 13
How to Create Multi Line Uisegmentedcontrol
Swift: How to Draw Shapes and Detect Whether They Intersect
Regex to Get String Between Two % Characters
Change Status Bar Text Colour from White iOS 7/Xcode 5
Convert Arabic String to English Number in Swift
How to Change Device Orientation Programmatically in Swift
Making Uitableview with Embedded Uicollectionview Using Uitableviewautomaticdimension
Apple Watchkit Simulator Issue: Sperrorinvalidbundlenogizmobinarymessage