curve in tabbar view and bottom popup not working? - swift
Hello all, I tried to add arc for UIBezierPath I could not able to get the exact curve,
here is my code here I have added the bezier path for the added curve from the center position.
#IBDesignable
class MyTabBar: UITabBar {
private var shapeLayer: CALayer?
private func addShape() {
let shapeLayer = CAShapeLayer()
shapeLayer.path = createPath()
shapeLayer.strokeColor = UIColor.lightGray.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = 1.0
//The below 4 lines are for shadow above the bar. you can skip them if you do not want a shadow
shapeLayer.shadowOffset = CGSize(width:0, height:0)
shapeLayer.shadowRadius = 10
shapeLayer.shadowColor = UIColor.gray.cgColor
shapeLayer.shadowOpacity = 0.3
if let oldShapeLayer = self.shapeLayer {
self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer)
} else {
self.layer.insertSublayer(shapeLayer, at: 0)
}
self.shapeLayer = shapeLayer
}
override func draw(_ rect: CGRect) {
self.addShape()
}
func createPath() -> CGPath {
let height: CGFloat = 37.0
let path = UIBezierPath()
let centerWidth = self.frame.width / 2
path.move(to: CGPoint(x: 0, y: 0)) // start top left
path.addLine(to: CGPoint(x: (centerWidth - height * 2), y: 0)) // the beginning of the trough
path.addCurve(to: CGPoint(x: centerWidth, y: height),
controlPoint1: CGPoint(x: (centerWidth - 30), y: 0), controlPoint2: CGPoint(x: centerWidth - 35, y: height))
path.addCurve(to: CGPoint(x: (centerWidth + height * 2), y: 0),
controlPoint1: CGPoint(x: centerWidth + 35, y: height), controlPoint2: CGPoint(x: (centerWidth + 30), y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: 0))
path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
path.addLine(to: CGPoint(x: 0, y: self.frame.height))
path.close()
return path.cgPath
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard !clipsToBounds && !isHidden && alpha > 0 else { return nil }
for member in subviews.reversed() {
let subPoint = member.convert(point, from: self)
guard let result = member.hitTest(subPoint, with: event) else { continue }
return result
}
return nil
}
}
this is tab bar controller added plus button in center view center, and the when tap the plus button to add the curve based popup should show, I don't know how to add curve based popup.
class TabbarViewController: UITabBarController,UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.navigationController?.navigationBar.isHidden = true
setupMiddleButton()
}
// TabBarButton – Setup Middle Button
func setupMiddleButton() {
let middleBtn = UIButton(frame: CGRect(x: (self.view.bounds.width / 2)-25, y: -20, width: 50, height: 50))
middleBtn.setImage(UIImage(named: "PlusBtn"), for: .normal)
self.tabBar.addSubview(middleBtn)
middleBtn.addTarget(self, action: #selector(self.menuButtonAction), for: .touchUpInside)
self.view.layoutIfNeeded()
}
// Menu Button Touch Action
#objc func menuButtonAction(sender: UIButton) {
//show the popUp
}
}
Please share me the findings & share me your refreance
Thanks
New edit:
I created a general-purpose method that will generate polygons with a mixture of sharp and rounded corners of different radii. I used it to create a project with a look rather like what you are after. You can download it from Github:
https://github.com/DuncanMC/CustomTabBarController.git
Here's what it looks like:
Note that the area of the tab's view controller that extends into the tab bar controller does not get taps. If you try to tap there, it triggers the tab bar controller. You will have to do some more tinkering to get that part to work.
Ultimately you may have to create a custom UITabBar (or UITabBar-like component) and possibly a custom parent view controller that acts like a UITabBar, in order to get what you want.
The method that creates polygon paths is called buildPolygonPathFrom(points:defaultCornerRadius:)
It takes an array of PolygonPoint structs. Those are defined like this:
/// A struct describing a single vertex in a polygon. Used in building polygon paths with a mixture of rounded an sharp-edged vertexes.
public struct PolygonPoint {
let point: CGPoint
let isRounded: Bool
let customCornerRadius: CGFloat?
init(point: CGPoint, isRounded: Bool, customCornerRadius: CGFloat? = nil) {
self.point = point
self.isRounded = isRounded
self.customCornerRadius = customCornerRadius
}
init(previousPoint: PolygonPoint, isRounded: Bool) {
self.init(point: previousPoint.point, isRounded: isRounded, customCornerRadius: previousPoint.customCornerRadius)
}
}
The code to build the path for the custom tab bar looks like this:
func tabBarMaskPath() -> CGPath? {
let width = bounds.width
let height = bounds.height
guard width > 0 && height > 0 else { return nil }
let dentRadius: CGFloat = 35
let cornerRadius: CGFloat = 20
let topFlatPartWidth = (width - dentRadius * 2.0) / 2
let polygonPoints = [
PolygonPoint(point: CGPoint(x: 0, y: 0), // Point 0
isRounded: true,
customCornerRadius: cornerRadius),
PolygonPoint(point: CGPoint(x: 0, y: height), // Point 1
isRounded: false),
PolygonPoint(point: CGPoint(x: width, y: height), // Point 2
isRounded: false),
PolygonPoint(point: CGPoint(x: width, y: 0), // Point 3
isRounded: true,
customCornerRadius: cornerRadius),
PolygonPoint(point: CGPoint(x: topFlatPartWidth + dentRadius * 2, y: 0), // Point 4
isRounded: true,
customCornerRadius: cornerRadius),
PolygonPoint(point: CGPoint(x: topFlatPartWidth + dentRadius * 2, y: dentRadius + cornerRadius), // Point 5
isRounded: true,
customCornerRadius: dentRadius),
PolygonPoint(point: CGPoint(x: topFlatPartWidth , y: dentRadius + cornerRadius), // Point 6
isRounded: true,
customCornerRadius: dentRadius),
PolygonPoint(point: CGPoint(x: topFlatPartWidth , y: 0), // Point 7
isRounded: true,
customCornerRadius: cornerRadius),
]
return buildPolygonPathFrom(points: polygonPoints, defaultCornerRadius: 0)
}
Previous answer:
I just tried it, and it is possible to subclass UITabBar. I created a subclass of UITabBar where I use a mask layer to cut a circular "notch" out of the top of the tab bar. The code is below. It looks like the screenshot below. It isn't quite what you're after, but it's a start:
(The background color for the "Page 1" view controller is set to light gray, and you can see that color showing through in the "notch" I cut out of the tab bar.)
//
// CustomTabBar.swift
// TabBarController
//
// Created by Duncan Champney on 3/31/21.
//
import UIKit
class CustomTabBar: UITabBar {
var maskLayer = CAShapeLayer()
override var frame: CGRect {
didSet {
configureMaskLayer()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
configureMaskLayer()
self.layer.mask = maskLayer
self.layer.borderWidth = 0
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configureMaskLayer()
self.layer.mask = maskLayer
}
func configureMaskLayer() {
let rect = layer.bounds
maskLayer.frame = rect
let circleBoxSize = rect.size.height * 1.25
maskLayer.fillRule = .evenOdd
let path = UIBezierPath(rect: rect)
let circleRect = CGRect(x: rect.size.width/2 - circleBoxSize / 2,
y: -circleBoxSize/2,
width: circleBoxSize,
height: circleBoxSize)
let circle = UIBezierPath.init(ovalIn: circleRect)
path.append(circle)
maskLayer.path = path.cgPath
maskLayer.fillColor = UIColor.white.cgColor // Any opaque color works and has no effect
}
}
Edit:
To draw your popover view you'll need to create a filled path that shape. You'll have to construct a custom shape like that with a combination of lines and arcs. I suggest using a CGMutablePath and the method addArc(tangent1End:tangent2End:radius:transform:) since that enables you to provide endpoints rather than angles.
Edit #2:
Another part of the puzzle:
Here is a custom UIView subclass that masks itself in the shape you're after
//
// ShapeWithTabView.swift
// ShapeWithTab
//
// Created by Duncan Champney on 4/1/21.
//
import UIKit
class ShapeWithTabView: UIView {
var cornerRadius: CGFloat = 20
var tabRadius: CGFloat = 60
var tabExtent: CGFloat = 0
var shapeLayer = CAShapeLayer()
var maskLayer = CAShapeLayer()
func buildShapeLayerPath() -> CGPath {
let boxWidth = min(bounds.size.width - 40, 686)
let boxHeight = min(bounds.size.height - 40 - tabRadius * 2 - tabExtent, 832)
// These are the corners of the view's primary rectangle
let point1 = CGPoint(x: 0, y: boxHeight)
let point2 = CGPoint(x: 0, y: 0)
let point3 = CGPoint(x: boxWidth, y: 0)
let point4 = CGPoint(x: boxWidth, y: boxHeight)
// These are the corners of the "tab" that extends outside the view's normal bounds.
let tabPoint1 = CGPoint(x: boxWidth / 2 + tabRadius, y: boxHeight)
let tabPoint2 = CGPoint(x: boxWidth / 2 + tabRadius, y: boxHeight + tabExtent + tabRadius * 2 )
let tabPoint3 = CGPoint(x: boxWidth / 2 - tabRadius, y: boxHeight + tabExtent + tabRadius * 2)
let tabPoint4 = CGPoint(x: boxWidth / 2 - tabRadius , y: boxHeight)
let path = CGMutablePath()
path.move(to: CGPoint(x: 0, y: boxHeight - cornerRadius))
path.addArc(tangent1End: point2,
tangent2End: point3,
radius: cornerRadius)
path.addArc(tangent1End: point3,
tangent2End: point4,
radius: cornerRadius)
path.addArc(tangent1End: point4,
tangent2End: point1,
radius: cornerRadius)
//
path.addArc(tangent1End: tabPoint1,
tangent2End: tabPoint2,
radius: tabRadius)
path.addArc(tangent1End: tabPoint2,
tangent2End: tabPoint3,
radius: tabRadius)
path.addArc(tangent1End: tabPoint3,
tangent2End: tabPoint4,
radius: tabRadius)
path.addArc(tangent1End: tabPoint4,
tangent2End: point1,
radius: tabRadius)
path.addArc(tangent1End: point1,
tangent2End: point2,
radius: cornerRadius)
return path
}
func doInitSetup() {
self.layer.addSublayer(shapeLayer)
self.layer.mask = maskLayer
backgroundColor = .lightGray
//Configure a shape layer to draw an outline
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 2
//Configure a mask layer to mask the view to our custom shape
maskLayer.fillColor = UIColor.white.cgColor
maskLayer.strokeColor = UIColor.white.cgColor
maskLayer.lineWidth = 2
}
override init(frame: CGRect) {
super.init(frame: frame)
self.doInitSetup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.doInitSetup()
}
public func updateShapeLayerPath() {
let path = buildShapeLayerPath()
shapeLayer.path = path
maskLayer.path = path
}
override var frame: CGRect {
didSet {
print("New frame = \(frame)")
shapeLayer.frame = layer.bounds
}
}
}
Combined with the modified tab bar from above, it looks like the image below. The final task is to get the custom view sized and positioned correctly, and have it land on top of the tab bar.
Related
Custom Tab Bar, with central button which will be hide by pressed index
How I can create Custom tab bar with 4 items and one FAB in center BUT Fab button show only when i press on index 3, for index 0 .. 3 fab.isHidden = true. I am don't need animation that button if shift, but in fill be plus), only show / hide. For creating custom tab bat I am use this guide https://medium.com/better-programming/draw-a-custom-ios-tabbar-shape-27d298a7f4fa, I am need create tab bar like this , I am try maenter image description hereny other way but can't solved problem. Custom tab bar which needed I am try this way import UIKit #IBDesignable class CustomizedTabBar: UITabBar { private var shapeLayer: CALayer? private func addShape() { let shapeLayer = CAShapeLayer() shapeLayer.path = createPath() shapeLayer.strokeColor = UIColor.red.cgColor shapeLayer.fillColor = UIColor.white.cgColor shapeLayer.lineWidth = 1.0 if let oldShapeLayer = self.shapeLayer { self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer) } else { self.layer.insertSublayer(shapeLayer, at: 0) } self.shapeLayer = shapeLayer } override func draw(_ rect: CGRect) { self.addShape() } func createPath() -> CGPath { let height: CGFloat = 37.0 let path = UIBezierPath() let centerWidth = self.frame.width / 2 path.move(to: CGPoint(x: 0, y: 0)) // start top left path.addLine(to: CGPoint(x: (centerWidth - height * 2), y: 0)) // the beginning of the trough //// // first curve down // path.addCurve(to: CGPoint(x: centerWidth, y: height), controlPoint1: CGPoint(x: (centerWidth - 25), y: 0), controlPoint2: CGPoint(x: centerWidth - 35, y: height)) // //// // second curve up // path.addCurve(to: CGPoint(x: (centerWidth + height * 1.0), y: 0), // controlPoint1: CGPoint(x: centerWidth + 25, y: height), controlPoint2: CGPoint(x: (centerWidth + 30), y: 0)) // cirle inside tab bar path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: height, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false) // complete the rect path.addLine(to: CGPoint(x: self.frame.width, y: 0)) path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height)) path.addLine(to: CGPoint(x: 0, y: self.frame.height)) path.close() return path.cgPath } override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { let buttonRadius: CGFloat = 35 return abs(self.center.x - point.x) > buttonRadius || abs(point.y) > buttonRadius } func createPathCircle() -> CGPath { let radius: CGFloat = 37.0 let path = UIBezierPath() let centerWidth = self.frame.width / 2 path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: (centerWidth - radius * 2), y: 0)) path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: radius, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false) path.addLine(to: CGPoint(x: self.frame.width, y: 0)) path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height)) path.addLine(to: CGPoint(x: 0, y: self.frame.height)) path.close() return path.cgPath } } extension CGFloat { var degreesToRadians: CGFloat { return self * .pi / 180 } var radiansToDegrees: CGFloat { return self * 180 / .pi } } And second way import UIKit class MainTabBar: UITabBar { var checkState: Bool = true public var middleButton = UIButton() override func awakeFromNib() { super.awakeFromNib() guard let tabItems = items else { return } tabItems[1].titlePositionAdjustment = UIOffset(horizontal: -10, vertical: 0) tabItems[2].titlePositionAdjustment = UIOffset(horizontal: 10, vertical: 0) setupMiddleButton() } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if self.isHidden { return super.hitTest(point, with: event) } let from = point let to = middleButton.center return sqrt((from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y)) <= 39 ? middleButton : super.hitTest(point, with: event) } override func layoutSubviews() { super.layoutSubviews() middleButton.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: 5) } func setupMiddleButton() { middleButton.frame.size = CGSize(width: 70, height: 70) middleButton.layer.cornerRadius = 35 middleButton.layer.masksToBounds = true middleButton.layer.borderWidth = 8 middleButton.layer.borderColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) middleButton.backgroundColor = #colorLiteral(red: 0.2447069331, green: 0.850134835, blue: 0.1531122658, alpha: 1) middleButton.setImage(UIImage(named: "plus.png"), for: .normal) middleButton.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: 0) middleButton.addTarget(self, action: #selector(test), for: .touchUpInside) addSubview(middleButton) } #objc func test() { print("my name is jeff") } } and I am try this way from article https://equaleyes.com/blog/2017/09/04/the-common-raised-center-button-problems-in-tabbar/ but it's not working for me, also I am read too many info from WWW and don't get answer(
I am was created this tab bar, ours need few steps. Create ViewController and Embed in "TabBarController", then need create TWO class first for "UITabBar" this class contain shape and what you want with "UITabBar", second class for "UITabBarController" for switch between ViewControllers inside we can add animation.... It's need because, my TabBar have 4 tabs and only on LAST tabs I am have Central FAB button with animation, and I am should animate position of my 2 and 3 ui tab bar element when button is appear. Class for "UITabBar" import UIKit #IBDesignable class CustomizedTabBar: UITabBar { // MARK:- Variables - #objc public var centerButtonActionHandler: ()-> () = {} #IBInspectable public var centerButton: UIButton? #IBInspectable public var centerButtonColor: UIColor? #IBInspectable public var centerButtonHeight: CGFloat = 50.0 #IBInspectable public var padding: CGFloat = 5.0 #IBInspectable public var buttonImage: UIImage? #IBInspectable public var buttonTitle: String? #IBInspectable public var tabbarColor: UIColor = UIColor.lightGray #IBInspectable public var unselectedItemColor: UIColor = .init(red: 0.58, green: 0.61, blue: 0.66, alpha: 1.0) #IBInspectable public var selectedItemColor: UIColor = UIColor.black public var arc: Bool = true { didSet { self.setNeedsDisplay() } } private var shapeLayer: CALayer? private func addShape() { let shapeLayer = CAShapeLayer() shapeLayer.path = createPath() shapeLayer.strokeColor = UIColor.white.cgColor shapeLayer.fillColor = #colorLiteral(red: 0.96, green: 0.96, blue: 0.96, alpha: 1) shapeLayer.lineWidth = 1.0 if let oldShapeLayer = self.shapeLayer { self.layer.replaceSublayer(oldShapeLayer, with: shapeLayer) } else { self.layer.insertSublayer(shapeLayer, at: 0) } self.shapeLayer = shapeLayer self.tintColor = centerButtonColor self.unselectedItemTintColor = unselectedItemColor self.tintColor = selectedItemColor self.setupMiddleButton() } override func draw(_ rect: CGRect) { self.addShape() } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { guard !clipsToBounds && !isHidden && alpha > 0 else { return nil } for member in subviews.reversed() { let subPoint = member.convert(point, from: self) guard let result = member.hitTest(subPoint, with: event) else { continue } return result } return nil } func createPath() -> CGPath { let padding: CGFloat = 5.0 let centerButtonHeight: CGFloat = 53.0 let f = CGFloat(centerButtonHeight / 2.0) + padding let h = frame.height let w = frame.width let halfW = frame.width/2.0 let r = CGFloat(18) let path = UIBezierPath() path.move(to: .zero) if (!arc) { path.addLine(to: CGPoint(x: halfW-f-(r/2.0), y: 0)) path.addQuadCurve(to: CGPoint(x: halfW-f, y: (r/2.0)), controlPoint: CGPoint(x: halfW-f, y: 0)) path.addArc(withCenter: CGPoint(x: halfW, y: (r/2.0)), radius: f, startAngle: .pi, endAngle: 0, clockwise: false) path.addQuadCurve(to: CGPoint(x: halfW+f+(r/2.0), y: 0), controlPoint: CGPoint(x: halfW+f, y: 0)) } path.addLine(to: CGPoint(x: w, y: 0)) path.addLine(to: CGPoint(x: w, y: h)) path.addLine(to: CGPoint(x: 0.0, y: h)) path.close() return path.cgPath } private func setupMiddleButton() { centerButton = UIButton(frame: CGRect(x: (self.bounds.width / 2)-(centerButtonHeight/2), y: -16, width: centerButtonHeight, height: centerButtonHeight)) centerButton!.setNeedsDisplay() centerButton!.layer.cornerRadius = centerButton!.frame.size.width / 2.0 centerButton!.setTitle(buttonTitle, for: .normal) centerButton!.setImage(UIImage(named: "plus"), for: .normal) centerButton!.backgroundColor = .init(red: 0.07, green: 0.83, blue: 0.05, alpha: 1.0) centerButton!.tintColor = UIColor.white self.centerButton!.isHidden = true if (!self.arc) { DispatchQueue.main.async { UIView.transition(with: self.centerButton!, duration: 1, options: .transitionCrossDissolve, animations: { self.centerButton!.isHidden = false }) } } //add to the tabbar and add click event self.addSubview(centerButton!) centerButton!.addTarget(self, action: #selector(self.centerButtonAction), for: .touchUpInside) } override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { let buttonRadius: CGFloat = 35 return abs(self.center.x - point.x) > buttonRadius || abs(point.y) > buttonRadius } func createPathCircle() -> CGPath { let radius: CGFloat = 37.0 let path = UIBezierPath() let centerWidth = self.frame.width / 2 path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: (centerWidth - radius * 2), y: 0)) path.addArc(withCenter: CGPoint(x: centerWidth, y: 0), radius: radius, startAngle: CGFloat(180).degreesToRadians, endAngle: CGFloat(0).degreesToRadians, clockwise: false) path.addLine(to: CGPoint(x: self.frame.width, y: 0)) path.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height)) path.addLine(to: CGPoint(x: 0, y: self.frame.height)) path.close() return path.cgPath } // Menu Button Touch Action #objc func centerButtonAction(sender: UIButton) { self.centerButtonActionHandler() } } extension CGFloat { var degreesToRadians: CGFloat { return self * .pi / 180 } var radiansToDegrees: CGFloat { return self * 180 / .pi } } And class for UITabBarController import UIKit class MyTabBarController: UITabBarController { override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) { let myTabBar = tabBar as! CustomizedTabBar if (myTabBar.items?[3] == item) { myTabBar.arc = false } else { myTabBar.arc = true } } }
Slant one edge of rectangle UIBezierPath
I'm trying to slant one edge of a UIBezierPath in Swift. I believe you do this by using move on one of the edges. Similar to the below - let offset: CGFloat = 60.0; let path = UIBezierPath() let width = self.bounds.width - offset let upperLeftPoint = CGPoint(x: self.bounds.origin.x + width + offset, y: self.bounds.origin.y) let upperRightPoint = CGPoint(x: self.bounds.origin.x, y: self.bounds.origin.y) let lowerRightPoint = CGPoint(x: self.bounds.origin.x, y: self.bounds.size.height) let lowerLeftPoint = CGPoint(x: width - offset, y: self.bounds.size.height) path.move(to: upperLeftPoint) path.addLine(to: upperRightPoint) path.addLine(to: lowerRightPoint) path.addLine(to: lowerLeftPoint) path.addLine(to: upperLeftPoint) // Close the path. This will create the last line automatically. path.close() UIColor.red.setFill() path.fill() let shapeLayer = CAShapeLayer() shapeLayer.path = path.cgPath self.layer.mask = shapeLayer; However this isn't quite what I'm trying to achieve. Below is what I'm attempting to achieve.
I achieved this shape by doing the following - class Header: UIView { var path: UIBezierPath! override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = UIColor.red } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } func createHeader() { // Drawing code // Get Height and Width let layerHeight = layer.frame.height let layerWidth = layer.frame.width // Create Path let bezierPath = UIBezierPath() // Points let pointA = CGPoint(x: 0, y: 0) let pointB = CGPoint(x: layerWidth, y: 0) let pointC = CGPoint(x: layerWidth, y: layerHeight*2/3) let pointD = CGPoint(x: 0, y: layerHeight) // Draw the path bezierPath.move(to: pointA) bezierPath.addLine(to: pointB) bezierPath.addLine(to: pointC) bezierPath.addLine(to: pointD) bezierPath.close() // Mask to Path let shapeLayer = CAShapeLayer() shapeLayer.path = bezierPath.cgPath layer.mask = shapeLayer } override func draw(_ rect: CGRect) { // self.createRectangle() self.createHeader() } }
Swift 4 - Create UIView with 2 background colors
I have a UIView having equal width/height as of its superview. This is what I want to get. For the time being I have used a static background image. Any ideas?
Result Code import UIKit #IBDesignable public class AngleView: UIView { #IBInspectable public var fillColor: UIColor = .blue { didSet { setNeedsLayout() } } var points: [CGPoint] = [ .zero, CGPoint(x: 1, y: 0), CGPoint(x: 1, y: 0.4), CGPoint(x: 0, y: 0.5) ] { didSet { setNeedsLayout() } } private lazy var shapeLayer: CAShapeLayer = { let _shapeLayer = CAShapeLayer() self.layer.insertSublayer(_shapeLayer, at: 0) return _shapeLayer }() override public func layoutSubviews() { shapeLayer.fillColor = fillColor.cgColor guard points.count > 2 else { shapeLayer.path = nil return } let path = UIBezierPath() path.move(to: convert(relativePoint: points[0])) for point in points.dropFirst() { path.addLine(to: convert(relativePoint: point)) } path.close() shapeLayer.path = path.cgPath } private func convert(relativePoint point: CGPoint) -> CGPoint { return CGPoint(x: point.x * bounds.width + bounds.origin.x, y: point.y * bounds.height + bounds.origin.y) } } Source Just modified the values :D
swift 4: you can instead ,of making a view with several background colors, add sublayer to your view. //connect your view. #IBOutlet weak var yourView: UIView! override func viewDidLoad() { super.viewDidLoad() //add a shape let shape = CAShapeLayer() self.yourView.layer.addSublayer(shape) shape.strokeColor = UIColor.black.cgColor shape.fillColor = UIColor.black.cgColor // add your path // you can simply adjust the points. let path = UIBezierPath() path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: 0, y: (3*yourView.bounds.height)/4)) path.addLine(to: CGPoint(x:self.yourView.bounds.width , y: yourView.bounds.height/2)) path.addLine(to: CGPoint(x: self.yourView.bounds.width, y: 0)) path.close() shape.path = path.cgPath }
How to cut edges of a uiview in swift
I attached the View image. I want to achieve the small cut in bottom between the buy button and above flight information view.
I think the easiest way would be to create 2 circles as plain UIView instances and set their center as the left and right edges of the parent view respectively. Since you set clipsToBounds to true, they will be clipped and only half of them will be visible on the screen. public class TestView: UIView { private let leftCircle = UIView(frame: .zero) private let rightCircle = UIView(frame: .zero) public var circleY: CGFloat = 0 public var circleRadius: CGFloat = 0 public override init(frame: CGRect) { super.init(frame: frame) clipsToBounds = true addSubview(leftCircle) addSubview(rightCircle) } public override func layoutSubviews() { super.layoutSubviews() leftCircle.frame = CGRect(x: -circleRadius, y: circleY, width: circleRadius * 2 , height: circleRadius * 2) leftCircle.layer.masksToBounds = true leftCircle.layer.cornerRadius = circleRadius rightCircle.frame = CGRect(x: bounds.width - circleRadius, y: circleY, width: circleRadius * 2 , height: circleRadius * 2) rightCircle.layer.masksToBounds = true rightCircle.layer.cornerRadius = circleRadius } } I've created a sample project demonstrating that. Here is how it looks in my simulator (iPhone SE 11.2):
I had to do this with shadows. I tried creating a layer and subtracting another layer from it using evenOdd fillRule, however that didn't work since I need a specific path for shadows and evenOdd applies to the filling in the path instead. In the end I just created the path manually func setShadowPath() { let path = UIBezierPath() path.move(to: bounds.origin) path.addLine(to: CGPoint(x: cutoutView.frame.minX, y: bounds.minY)) path.addArc(withCenter: CGPoint(x: cutoutView.frame.midX, y: bounds.minY), radius: cutoutView.bounds.width/2, startAngle: .pi, endAngle: 0, clockwise: false) path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY)) path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY)) path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY)) path.close() layer.shadowPath = path.cgPath } I created a "cutoutView" in my xib so I could trace around it easily. That makes the shadow the correct shape, and then in order to create the cut itself I just created a layer using that same path func setupBackground() { let backgroundLayer = CAShapeLayer() backgroundLayer.path = layer.shadowPath backgroundLayer.fillColor = UIColor.white.cgColor layer.insertSublayer(backgroundLayer, at: 0) }
iOS Camera Customization: How to implement Grid Lines?
I am able to use the flash feature, access the photo gallery, and use the rear/front camera. I would like to implement grid lines that display when the user is taking a photo. Any ideas?
Create a UIImageView to use for the cameraOverlayView. Assuming you've got a UIImagePickerController named yourImagePickerController and also that you've got an image file named overlay.png as your 'grid lines'. When making your grid line image file, be sure to use a transparent background - not opaque white. UIImageView *overlayImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"overlay.png"]]; CGRect overlayRect = CGRectMake(0, 0, overlayImage.image.size.width, overlayImage.image.size.height); [overlayImage setFrame:overlayRect]; [yourImagePickerController setCameraOverlayView:overlayImage];
It's pretty easy with UIBezierPath. This is how you can achieve. Save this code in a file called GridView.swift. import Foundation import UIKit class GridView: UIView { override func draw(_ rect: CGRect) { let firstColumnPath = UIBezierPath() firstColumnPath.move(to: CGPoint(x: bounds.width / 3, y: 0)) firstColumnPath.addLine(to: CGPoint(x: bounds.width / 3, y: bounds.height)) let firstColumnLayer = gridLayer() firstColumnLayer.path = firstColumnPath.cgPath layer.addSublayer(firstColumnLayer) let secondColumnPath = UIBezierPath() secondColumnPath.move(to: CGPoint(x: (2 * bounds.width) / 3, y: 0)) secondColumnPath.addLine(to: CGPoint(x: (2 * bounds.width) / 3, y: bounds.height)) let secondColumnLayer = gridLayer() secondColumnLayer.path = secondColumnPath.cgPath layer.addSublayer(secondColumnLayer) let firstRowPath = UIBezierPath() firstRowPath.move(to: CGPoint(x: 0, y: bounds.height / 3)) firstRowPath.addLine(to: CGPoint(x: bounds.width, y: bounds.height / 3)) let firstRowLayer = gridLayer() firstRowLayer.path = firstRowPath.cgPath layer.addSublayer(firstRowLayer) let secondRowPath = UIBezierPath() secondRowPath.move(to: CGPoint(x: 0, y: ( 2 * bounds.height) / 3)) secondRowPath.addLine(to: CGPoint(x: bounds.width, y: ( 2 * bounds.height) / 3)) let secondRowLayer = gridLayer() secondRowLayer.path = secondRowPath.cgPath layer.addSublayer(secondRowLayer) } private func gridLayer() -> CAShapeLayer { let shapeLayer = CAShapeLayer() shapeLayer.strokeColor = UIColor(white: 1.0, alpha: 0.3).cgColor shapeLayer.frame = bounds shapeLayer.fillColor = nil return shapeLayer } } Usage: func addGridView(cameraView: UIView) { let horizontalMargin = cameraView.bounds.size.width / 4 let verticalMargin = cameraView.bounds.size.height / 4 let gridView = GridView() gridView.translatesAutoresizingMaskIntoConstraints = false cameraView.addSubview(gridView) gridView.backgroundColor = UIColor.clear gridView.leftAnchor.constraint(equalTo: cameraView.leftAnchor, constant: horizontalMargin).isActive = true gridView.rightAnchor.constraint(equalTo: cameraView.rightAnchor, constant: -1 * horizontalMargin).isActive = true gridView.topAnchor.constraint(equalTo: cameraView.topAnchor, constant: verticalMargin).isActive = true gridView.bottomAnchor.constraint(equalTo: cameraView.bottomAnchor, constant: -1 * verticalMargin).isActive = true }
As far as the documentation goes it doesn't state whether the grid lines provided by Apple is actually a shared method but as it's not mentioned I'd say not, but you can implement your own with the cameraOverlayView.