BlurView below a button with fading edges - ios

I created a button on top of my mapView.
In order to make that Button more visible I added a blur view below that button.
I don't like the sharp edges of my blur view.
How do I make the blur fade out slowly transitioning into the mapView?
EDIT: To be more specific. I mean a fading blurred gradient with round corners.

I think this can help you, first you need subclass your button and add this code in drawRect and replace UIColor.blueColor().CGColor by yourColor.CGColor
class UICustomBackgroundButton: UIButton {
override func draw(_ rect: CGRect) {
// Drawing code
super.draw(rect)
let ctx = UIGraphicsGetCurrentContext();
let path = CGPath(roundedRect: rect.insetBy(dx: self.frame.size.height/4, dy: self.frame.size.height/4) , cornerWidth: self.frame.size.height/8, cornerHeight: self.frame.size.height/8, transform: nil)
ctx?.setShadow(offset: CGSize.zero, blur: self.frame.size.height/4, color: UIColor.blue.cgColor);
ctx?.setFillColor(UIColor.blue.cgColor);
//if number of cicles is our glow is darker
for _ in 0..<6
{
ctx?.addPath(path);
ctx?.fillPath();
}
}
}
I Hope this helps you.
and this is how it look

Have you tried the cornerRadius property? That should remove the sharp edges

Related

Swift ios cut out rounded rect from view allowing colour changes

I'm using this approach to cut out a rounded rect "window" from a background view:
override func draw(_ rect: CGRect) {
guard let rectsArray = rectsArray else {
return
}
for holeRect in rectsArray {
let holeRectIntersection = rect.intersection(holeRect)
if let context = UIGraphicsGetCurrentContext() {
let roundedWindow = UIBezierPath(roundedRect: holeRect, cornerRadius: 15.0)
if holeRectIntersection.intersects(rect) {
context.addPath(roundedWindow.cgPath)
context.clip()
context.clear(holeRectIntersection)
context.setFillColor(UIColor.clear.cgColor)
context.fill(holeRectIntersection)
}
}
}
}
In layoutSubviews() I update the background colour add my "window frame" rect:
override func layoutSubviews() {
super.layoutSubviews()
backgroundColor = self.baseMoodColour
isOpaque = false
self.rectsArray?.removeAll()
self.rectsArray = [dragAreaView.frame]
}
I'm adding the rect here because layoutSubviews() updates the size of the "window frame" (i.e., the rect changes after layoutSubviews() runs).
The basic mechanism works as expected, however, if I change the background colour, the cutout window fills with black. So I'm wondering how I can animate a background colour change with this kind of setup? That is, I want to animate the colour of the area outside the cutout window (the window remains clear).
I've tried updating backgroundColor directly, and also using didSet in the accessor of a custom colour variable in my UIView subclass, but both cause the same filling-in of the "window".
var baseMoodColour: UIColor {
didSet {
self.backgroundColor = baseMoodColour
self.setNeedsDisplay()
}
}
Try to use UIView.animate, you can check it here
UIView.animate(withDuration: 1.0, delay: 0.0, options: [.curveEaseOut], animations: {
self.backgroundColor = someNewColour
//Generally
//myView.backgroundColor = someNewColor
}, nil)
The problem in the short run is that that is simply what clear does if the background color is opaque. Just give your background color some transparency — even a tiny bit of transparency, so tiny that the human eye cannot perceive it — and now clear will cut a hole in the view.
For example, your code works fine if you set the view's background color to UIColor.green.withAlphaComponent(0.99).
By the way, you should delete the lines about UIColor.clear; that's a red herring. You should also cut the lines about the backgroundColor; you should not be repainting the background color into your context. They are two different things.
The problem in the long run is that what you're doing is not how to punch a hole in a view. You should be using a mask instead. That's the only way you're going to get the animation while maintaining the hole.
Answering my own question, based on #matt's suggestion (and linked example), I did it with a CAShapeLayer. There was an extra "hitch" in my requirements, since I have a couple of views on top of the one I needed to mask out. So, I did the masking like this:
func cutOutWindow() {
// maskedBackgroundView is an additional view, inserted ONLY for the mask
let r = self.maskedBackgroundView.bounds
// Adjust frame for dragAreaView's border
var dragSize = self.dragAreaView.frame.size
var dragPosition = self.dragAreaView.frame.origin
dragSize.width -= 6.0
dragSize.height -= 6.0
dragPosition.x += 3.0
dragPosition.y += 3.0
let r2 = CGRect(x: dragPosition.x, y: dragPosition.y, width: dragSize.width, height: dragSize.height)
let roundedWindow = UIBezierPath(roundedRect: r2, cornerRadius: 15.0)
let mask = CAShapeLayer()
let path = CGMutablePath()
path.addPath(roundedWindow.cgPath)
path.addRect(r)
mask.path = path
mask.fillRule = kCAFillRuleEvenOdd
self.maskedBackgroundView.layer.mask = mask
}
Then I had to apply the colour change to maskedBackgroundView.layer.backgroundColor (i.e., to the layer, not the view). With that in place, I get the cutout I need, with animatable colour changes. Thanks #matt for pointing me in the right direction.

Changing colour of path drawn in UIView draw(_rect)

I have drawn a circle using addArc in my UIView subclass' draw(_ rect: CGrect) function. It draws fine initially, but when some UI trigger occurs, I wanted to change the fill colour of the circle by changing the value of a isFilledIn property.
However, my circles do not change when the isFilledIn property is modified. The isFilledIn property and the draw method are both being called as expected, but the appearance of the circle does not change. It seems once the circles are drawn, their appearance is stuck.
var isFilledIn = false {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext() {
context.setLineWidth(outlineWidth)
myOutlineColor.setStroke()
if isFilledIn {
myFilledColor.setFill()
} else {
myEmptyColor.setFill()
}
let center = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
let radius = (frame.size.width - 10) / 2
context.addArc(center: center, radius: radius, startAngle: 0.0, endAngle: .pi * 2.0, clockwise: true)
context.drawPath(using: .fillStroke)
}
}
I tried adding a context.clear(rect) but that had no effect. I also considered saving the CGPath to a property and just trying to modify that instead of calling setNeedsDisplay but I wasn't sure how to apply the fill colour changes if all I have is a CGPath.
I realize there are other, potentially easier / more efficient ways to create circles (e.g. UIBezierPath, or a UIView with a corner radius), but I'm specifically trying to understand Core Graphics better and I want to understand why this doesn't work.
Was calling my view setup code in layoutSubviews, forgetting that that method may be called multiple times (and it was). So as Sulthan pointed out in the comments above, I did have overlapping views. There was a second circle on top of the one I was trying to redraw.
Adding an if statement inside layoutSubviews to prevent it from setting up the circle multiple times fixed it for me.
Thank you Sulthan.

Ios - Is there a way to draw a line in UIViewController without using code

I want to draw a line in UIViewController without using code, so like the constraints for the lines can be given in storyboard itself. if there is a way to do it, please tell me. Now i have drawn a line by placing an UIView in storyboard and used the following code to draw line on it. Is this the best way ?. Or do i have any better way ?. Thanks in advance.
class line: UIView {
override func draw(_ rect: CGRect) {
let aPath = UIBezierPath()
aPath.move(to: CGPoint(x:0, y:1))
aPath.addLine(to: CGPoint(x:500, y:1))
//Keep using the method addLineToPoint until you get to the one where about to close the path
aPath.close()
//If you want to stroke it with a red color
UIColor.blue.set()
aPath.stroke()
//If you want to fill it as well
aPath.fill()
}
}
Using Swift 3.0 and Xcode 8.2
Use #IBDesignable as follows. Make your view with height of one as suggested and the #IBDesignable will show your line in your storyboard.:
import UIKit
#IBDesignable class line: UIView {
override func draw(_ rect: CGRect) {
let aPath = UIBezierPath()
aPath.move(to: CGPoint(x: 0, y: 1))
aPath.addLine(to: CGPoint(x: 250, y: 1))
aPath.close()
UIColor.blue.set()
aPath.stroke()
}
}
Add a UIView with a height of 1 point.
Add constraints for it.

How to set border edges in HorizontalBarChartView

I need help creating this style BarChart, only the rounded end edges.
Here is an example:
I'm using the library "ios-charts" but it does not have this feature. Please let me know how to do this - rounded end edges and horizontal bar chart with negative values.
It's not that that hard to draw bars that like that yourself. Perhaps fork that library and improve it and make a push request. Here's some simple code that draws a red bar pointing to the right.
import UIKit
#IBDesignable
class RoundedBarView: UIView {
override func drawRect(rect: CGRect) {
let color = UIColor.redColor()
let barRect = CGRectMake(0.0,0.0,self.bounds.width, self.bounds.height)
var rectanglePath = UIBezierPath(roundedRect: barRect, byRoundingCorners: UIRectCorner.TopRight | UIRectCorner.BottomRight, cornerRadii: CGSizeMake(barRect.height/2.0, barRect.height/2.0))
rectanglePath.closePath()
color.setFill()
rectanglePath.fill()
}
}

Failing to stroke() the UIBezierPath

I wish to create a perfectly rounded rect (Circle) and paint it on the screen. I have tested my code in playground, and it successfully paints the UIBezierPath. It doesn't, however, successfully paint it in the iOS simulator. Here's the code I've been working on:
class Circles {
//Defining the rounded rect (Circle)
func roundRect(radius: CGFloat, angle: CGFloat) -> UIBezierPath {
//Creating the rounded the rectangle (Circle)
var roundedRect = UIBezierPath()
roundedRect.addArcWithCenter(CGPointZero, radius: radius,
startAngle: 0, endAngle: angle ,
clockwise: true)
return roundedRect
}
//Drawing the Bezier Path (Circle)
func drawRect(rect: UIBezierPath){
rect.moveToPoint(self.point)
UIColor.blackColor().setStroke()
rect.stroke()
}
//Giving the rounded rect (Circle) it's position
var point = CGPointMake(500, 500)
}
//Giving the rounded rect (Circle) it's size
var newRect = Circles().roundRect(200.0, angle: 7)
//Drawing the rounded rect (Circle)
Circles().drawRect(newRect)
I have seen some other posts with similar problems from a few years back, however they were in Objective-C, I tried translating but it was not of any use. I've also tried several other methods of painting the path on the screen but, again sadly, it was of no use. I tested it to make sure the functions are working with println statements, the issue is I don't know why the stroke is not activating. Thanks for reading, -Zach.
Here's the updated version using what Mike said:
class CircleView: UIView {
override func drawRect(rect: CGRect) {
// Creating the rectangle's size
var newRect = Circles().roundRect(200.0, angle: 7)
//Drawing the rectangle
Circles().drawRect(newRect)
}
//Holding all to do with the circle
class Circles {
//Defining the rounded rect (Circle)
func roundRect(radius: CGFloat, angle: CGFloat) -> UIBezierPath {
//Creating the rounded rect (Circle)
var roundedRect = UIBezierPath()
roundedRect.addArcWithCenter(CGPointZero, radius: radius,
startAngle: 0, endAngle: angle ,
clockwise: true)
return roundedRect
}
//Drawing the Bezier Path (Circle)
func drawRect(rect: UIBezierPath){
rect.moveToPoint(self.point)
UIColor.blackColor().setStroke()
UIColor.blackColor().setFill()
rect.stroke()
rect.fill()
}
//Giving the rounded rect (Circle) it's position
var point = CGPointMake(500, 500)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Generating the background
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "normalpaper.jpg"))
let circleView = CircleView(frame: self.view.bounds)
self.view.addSubview(circleView)
}
As you mentioned in the comments, you're calling drawRect from a UIViewController's viewDidLoad function. You don't have a valid drawing context there, so that's not going to work.
The easiest way to make this work is to create a UIView subclass with its own drawRect function which calls Circle().drawRect(...) and add that as a subview of your UIViewController's view.
Here's an example of this:
Note: I've made the custom view transparent by setting circleView.opaque = false so that the background you mentioned in the comments shows through.
class CircleView: UIView {
override func drawRect(rect: CGRect) {
// I just copied this directly from the original question
var newRect = Circles().roundRect(200.0, angle: 7)
Circles().drawRect(newRect)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Create a new CircleView that's the same size of this
// view controller's bounds and add it to the view hierarchy
let circleView = CircleView(frame: self.view.bounds)
circleView.opaque = false
self.view.addSubview(circleView)
}
}
Note: If you're going to be doing custom drawing, I highly recommend you read Drawing and Printing Guide for iOS. It'll teach you all the basics you need to make it work.

Resources