Using Drawn Shapes and Changing Their Features - swift

After spending a whole day trying to work out how to do it I have finally worked out a way of having non-rectangular buttons.
All three buttons at the bottom are on one button. The hitTest then works out which UIBezierPath the click was within then calls a function.
import UIKit
class ViewController: UIViewController {
let path = UIBezierPath()
let path2 = UIBezierPath()
let path3 = UIBezierPath()
let path4 = UIBezierPath()
#IBAction func clicked(_ sender: Any) {
print("Clicked")
}
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
drawShape()
let gesture = UITapGestureRecognizer(target: self, action: #selector (self.checkAction(sender:)))
self.button.addGestureRecognizer(gesture)
}
func drawShape(){
let buttonWidth = button.frame.width
let buttonHeight = buttonWidth * 1.23
path.move(to: CGPoint(x: 0, y: buttonHeight*0.85))
path.addLine(to: CGPoint(x: 0, y: buttonHeight))
path.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight))
path.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.68))
path.close()
path2.move(to: CGPoint(x: 0, y: buttonHeight*0.68))
path2.addLine(to: CGPoint(x: 0, y: buttonHeight*0.85))
path2.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.68))
path2.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.35))
path2.close()
path3.move(to: CGPoint(x: 0, y: buttonHeight*0.50))
path3.addLine(to: CGPoint(x: 0, y: buttonHeight*0.68))
path3.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.35))
path3.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.06))
path3.close()
path4.move(to: CGPoint(x: 0, y: buttonHeight*0.45))
path4.addLine(to: CGPoint(x: 0, y: buttonHeight*0.50))
path4.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.06))
path4.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.01))
path4.close()
}
func checkAction(sender : UITapGestureRecognizer) {
let location = sender.location(in: button)
print(location)
hitTest(tapLocation: location)
}
public func hitTest(tapLocation:CGPoint){
if path.contains(tapLocation){
print("Button3")
}
if path2.contains(tapLocation){
print("Button2")
}
if path3.contains(tapLocation){
print("Button1")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Then in the file linked to the button:
import UIKit
#IBDesignable
class PushButtonView: UIButton {
override func draw(_ rect: CGRect) {
let buttonWidth = self.frame.width
let buttonHeight = buttonWidth * 1.23
let color1 = hexStringToUIColor(hex: "#e0dfd5")
let color2 = hexStringToUIColor(hex: "#ef6461")
let color3 = hexStringToUIColor(hex: "#e4b363")
let color4 = hexStringToUIColor(hex: "#313638")
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: buttonHeight*0.85))
path.addLine(to: CGPoint(x: 0, y: buttonHeight))
path.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight))
path.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.68))
path.close()
color4.setFill()
path.fill()
let path2 = UIBezierPath()
path2.move(to: CGPoint(x: 0, y: buttonHeight*0.68))
path2.addLine(to: CGPoint(x: 0, y: buttonHeight*0.85))
path2.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.68))
path2.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.35))
path2.close()
color3.setFill()
path2.fill()
let path3 = UIBezierPath()
path3.move(to: CGPoint(x: 0, y: buttonHeight*0.50))
path3.addLine(to: CGPoint(x: 0, y: buttonHeight*0.68))
path3.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.35))
path3.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.06))
path3.close()
color2.setFill()
path3.fill()
let path4 = UIBezierPath()
path4.move(to: CGPoint(x: 0, y: buttonHeight*0.45))
path4.addLine(to: CGPoint(x: 0, y: buttonHeight*0.50))
path4.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.06))
path4.addLine(to: CGPoint(x: buttonWidth, y: buttonHeight*0.01))
path4.close()
color1.setFill()
path4.fill()
}
func hexStringToUIColor (hex:String) -> UIColor {
var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
if (cString.hasPrefix("#")) {
cString.remove(at: cString.startIndex)
}
if ((cString.characters.count) != 6) {
return UIColor.gray
}
var rgbValue:UInt32 = 0
Scanner(string: cString).scanHexInt32(&rgbValue)
return UIColor(
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
}
Everything works! It may not be the best way of doing it but after 5 hours of trying different things this is whats worked best. However I am now stuck with the problem of having no idea how to manipulate those shapes I have drawn in the external file. I am sure that this has been asked and answered before but I literally have no idea was to search for.
For example if it was javascript I would assume a 'path' drawn within the object 'button' would be accessed by self.button.path (or something similar), however, my understanding is Swift3 doesn't work like that. Am I correct? So if I wanted to change the backgroundColor of the NOTES button/shape with an animate how would I get this effect:
UIView.animate(withDuration: 1, animations: {
self.button.path.color = UIColor.red
}, completion: { finished in
if(finished){
//callFunction()
}
})
Is this possible, or once a path is drawn is it not a changeable object and has to be deleted and redrawn?

Related

iOS: Chat bubble change position of tail

I'm trying to achive this layout
[LAYOUT TO ACHIEVE]
and right now I have gradient part done, blur part done, and also bezier path mostly done. My question is that I can't replace TAIL from bottom right part to top left part (to be similar like in the picture).
[LAYOUT ACHIEVED]
My Code I wrote for acheiving:
public final class BubbleView: UIView {
public var configuration: Configuration = Configuration.defaultConfiguration {
didSet {
setNeedsLayout()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
super.backgroundColor = .clear
}
public override func draw(_ rect: CGRect) {
let hasBlurSubview = subviews.compactMap({$0}).contains(where: {$0 is UIVisualEffectView })
guard !hasBlurSubview else {
return
}
let lineWidth = configuration.lineWidth
let bezierPath = UIBezierPath()
bezierPath.lineWidth = lineWidth
let bottom = rect.height - lineWidth
let right = rect.width - lineWidth
let top = 0.0
let left = 0.0
bezierPath.move(to: CGPoint(x: right - 22, y: bottom))
bezierPath.addLine(to: CGPoint(x: 17 + lineWidth, y: bottom))
bezierPath.addCurve(to: CGPoint(x: left, y: bottom - 18), controlPoint1: CGPoint(x: 7.61 + lineWidth, y: bottom), controlPoint2: CGPoint(x: left, y: bottom - 7.61))
bezierPath.addLine(to: CGPoint(x: left, y: 17 + lineWidth))
bezierPath.addCurve(to: CGPoint(x: 17 + borderWidth, y: top), controlPoint1: CGPoint(x: left, y: 7.61 + lineWidth), controlPoint2: CGPoint(x: 7.61 + lineWidth, y: top))
bezierPath.addLine(to: CGPoint(x: right - 21, y: top))
bezierPath.addCurve(to: CGPoint(x: right - 4, y: 17 + lineWidth), controlPoint1: CGPoint(x: right - 11.61, y: top), controlPoint2: CGPoint(x: right - 4, y: 7.61 + lineWidth))
bezierPath.addLine(to: CGPoint(x: right - 4, y: bottom - 11))
bezierPath.addCurve(to: CGPoint(x: right, y: bottom), controlPoint1: CGPoint(x: right - 4, y: bottom - 1), controlPoint2: CGPoint(x: right, y: bottom))
bezierPath.addLine(to: CGPoint(x: right + 0.05, y: bottom - 0.01))
bezierPath.addCurve(to: CGPoint(x: right - 11.04, y: bottom - 4.04), controlPoint1: CGPoint(x: right - 4.07, y: bottom + 0.43), controlPoint2: CGPoint(x: right - 8.16, y: bottom - 1.06))
bezierPath.addCurve(to: CGPoint(x: right - 22, y: bottom), controlPoint1: CGPoint(x: right - 16, y: bottom), controlPoint2: CGPoint(x: right - 19, y: bottom))
bezierPath.close()
let blurEffect = UIVisualEffectView(effect: UIBlurEffect(style: .light))
blurEffect.alpha = configuration.blurAlpha
blurEffect.frame = bezierPath.bounds
let mask = CAShapeLayer()
mask.path = bezierPath.cgPath
blurEffect.layer.mask = mask
insertSubview(blurEffect, at: 0)
let gradient = CAGradientLayer()
gradient.frame = CGRect(origin: CGPoint.zero, size: size)
gradient.colors = configuration.borderGradientColors.map({$0.cgColor})
gradient.startPoint = configuration.borderGradientStartPoint
gradient.endPoint = configuration.borderGradientEndPoint
let shape = CAShapeLayer()
shape.lineWidth = lineWidth
shape.path = bezierPath.cgPath
shape.strokeColor = UIColor.black.cgColor
shape.fillColor = UIColor.clear.cgColor
gradient.mask = shape
blurEffect.layer.addSublayer(gradient)
}
}
public class Configuration {
static var defaultConfiguration: Configuration {
return .init(
lineWidth: 2.0,
blurAlpha: 0.95,
borderGradientColors: [Assets.Colors.white05.color, Assets.Colors.white01.color],
borderGradientStartPoint: CGPoint(x: 0.0, y: 0.5),
borderGradientEndPoint: CGPoint(x: 1.0, y: 0.5)
)
}
var lineWidth: CGFloat
var blurAlpha: CGFloat
var borderGradientColors: [UIColor]
var borderGradientStartPoint: CGPoint
var borderGradientEndPoint: CGPoint
init(
lineWidth: CGFloat,
blurAlpha: CGFloat,
borderGradientColors: [UIColor],
borderGradientStartPoint: CGPoint,
borderGradientEndPoint: CGPoint
) {
self.lineWidth = lineWidth
self.blurAlpha = blurAlpha
self.borderGradientColors = borderGradientColors
self.borderGradientStartPoint = borderGradientStartPoint
self.borderGradientEndPoint = borderGradientEndPoint
}
}

How to blur the border of the mask for an NSImage?

I am trying to blur the border of the mask for an NSImage in swift and I can't figure out, how to do this. The mask is supposed to be created from a CGMutablePath, so that it can be changed programmatically. This is what it looks like at the moment:
This is what it is supposed to look like:
The code for creating the mask with sharp edges is the following:
class MyView: NSView {
override func draw(_ dirtyRect: NSRect) {
let bottomLayer = CALayer()
bottomLayer.bounds = self.bounds
bottomLayer.position = CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2)
bottomLayer.contents = NSImage(named: "bottom.jpg")
bottomLayer.contentsGravity = .resize
self.layer?.addSublayer(bottomLayer)
let path = CGMutablePath()
path.move(to: CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2))
path.addCurve(to: CGPoint(x: 0.5*self.bounds.size.width, y: self.bounds.size.height), control1: CGPoint(x: 0.4*self.bounds.size.width, y: 0), control2: CGPoint(x: 0, y: self.bounds.size.height))
path.addCurve(to: CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2), control1: CGPoint(x: self.bounds.size.width, y: self.bounds.size.height), control2: CGPoint(x: 0.8*self.bounds.size.width, y: 0))
path.closeSubpath()
let maskLayer = CAShapeLayer()
maskLayer.path = path
maskLayer.fillRule = .evenOdd
maskLayer.borderWidth = 20
let topLayer = CALayer()
topLayer.bounds = self.bounds
topLayer.position = CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2)
topLayer.contents = NSImage(named: "top.jpg")
topLayer.contentsGravity = .resize
topLayer.masksToBounds = true
topLayer.mask = maskLayer
self.layer?.addSublayer(topLayer)
}
}
I hope there is a nice solution to this :)
Frederik
Try adding CAGradientLayer instead of CAShapeLayer:
Sample Source:
class MyView: NSView {
override func draw(_ dirtyRect: NSRect) {
let bottomLayer = CALayer()
bottomLayer.bounds = self.bounds
bottomLayer.position = CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2)
bottomLayer.contents = NSImage(named: "bottom")
bottomLayer.contentsGravity = .resize
self.layer?.addSublayer(bottomLayer)
let path = CGMutablePath()
path.move(to: CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2))
path.addCurve(to: CGPoint(x: 0.5*self.bounds.size.width, y: self.bounds.size.height), control1: CGPoint(x: 0.4*self.bounds.size.width, y: 0), control2: CGPoint(x: 0, y: self.bounds.size.height))
path.addCurve(to: CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2), control1: CGPoint(x: self.bounds.size.width, y: self.bounds.size.height), control2: CGPoint(x: 0.8*self.bounds.size.width, y: 0))
path.closeSubpath()
let maskLayer = CAGradientLayer()
maskLayer.shadowRadius = 4
maskLayer.shadowPath = path
maskLayer.shadowOpacity = 1
maskLayer.shadowOffset = CGSize.zero
maskLayer.shadowColor = NSColor.green.cgColor
let topLayer = CALayer()
topLayer.bounds = self.bounds
topLayer.position = CGPoint(x: self.bounds.size.width/2, y: self.bounds.size.height/2)
topLayer.contents = NSImage(named: "top")
topLayer.contentsGravity = .resize
topLayer.masksToBounds = true
topLayer.mask = maskLayer
self.layer?.addSublayer(topLayer)
}
}
Output

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
}
}
}

How to make a CAshapeLayer fit into a button?

I tried to create a function inside a custom UIButton class to add a shape to an existing button.
func drawStartButton(){
let shape = CAShapeLayer()
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 500, y: 0))
path.addLine(to: CGPoint(x: 500, y: -100))
path.addLine(to: CGPoint(x: 250, y: -50))
path.addLine(to: CGPoint(x: 0, y: -100))
path.addLine(to: CGPoint(x: 0, y: 0))
path.close()
shape.path = path.cgPath
shape.fillColor = redColor.cgColor
shape.frame = self.bounds
self.layer.addSublayer(shape)
}
So far no problem... BUT when i add the layer to the button, the layer is to big of course! How can i "autoresize" the layer to its button? I did expect something like
shape.frame = self.bounds
... but the path still keeps the same size as without the self.bounds.
Your best bet is to subclass the button and then layout your layer in layoutSubviews which gets called when your surrounding frame changes.
final class StartButton: UIButton {
private let shapeLayer = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
shapeLayer.frame = bounds
}
}
But I'm noticing that you are dictating a giant shape in terms of your CGPoints. You might have to subclass the layer, too, and redraw based on your bounds in layoutSublayers.
class CustomShapeCAshapeLayer: CAShapeLayer {
func shapeButton(width: CGFloat, height: CGFloat){
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: width, y: 0))
path.addLine(to: CGPoint(x: width, y: height + 30))
path.addLine(to: CGPoint(x: width/2, y: height))
path.addLine(to: CGPoint(x: 0, y: height + 30))
path.addLine(to: CGPoint(x: 0, y: 0))
path.close()
self.fillColor = redColor.cgColor
self.path = path.cgPath
}
}
main:
let layer = CustomShapeCAshapeLayer()
layer.shapeButton(width: startGameButton.frame.width, height: startGameButton.frame.height)
startGameButton.layer.addSublayer(layer)

How to create Right Angle View in Swift?

I am able to create triangle view in swift using below code-
class TriangleView: UIView {
override func draw(_ rect: CGRect) {
// Get Height and Width
let layerHeight = layer.frame.height
let layerWidth = layer.frame.width
// Create Path
let bezierPath = UIBezierPath()
// Draw Points
bezierPath.move(to: CGPoint(x: 0, y: layerHeight))
bezierPath.addLine(to: CGPoint(x: layerWidth, y: layerHeight))
bezierPath.addLine(to: CGPoint(x: layerWidth / 2, y: 0))
bezierPath.addLine(to: CGPoint(x: 0, y: layerHeight))
bezierPath.close()
// Apply Color
UIColor.red.setFill()
bezierPath.fill()
// Mask to Path
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierPath.cgPath
layer.mask = shapeLayer
}
}
override func viewDidLoad() {
super.viewDidLoad()
let triangle = TriangleView(frame: CGRect(x: 0, y: 0, width: 55 , height: 60))
triangle.backgroundColor = .white
triangle.transform = CGAffineTransform(rotationAngle: .pi * 4.49)
imageView.addSubview(triangle)
}
Now problem is that I want to change it to the right angle triangle using transform properties. How to achieve this?
Expected Result
Current Result
I hope you want this part.
Here is the code.
override func draw(_ rect: CGRect) {
let path = UIBezierPath.init()
// top left
path.move(to: .zero)
// top right
path.addLine(to: CGPoint(x: bounds.size.width,
y: 0))
// bottom left offset
path.addLine(to: CGPoint(x: bounds.size.width * 0.1,
y: bounds.size.height))
// bottom left
path.addLine(to: CGPoint(x: 0,
y: bounds.size.height))
// top left
path.close()
// change fill color here.
UIColor.black.setFill()
path.fill()
}
Result
How I drew
Modification
If you change the code UIColor.black.setFill() to
let fillColor = UIColor(red: 0xF9/0xff, green: 0x0D/0xff, blue: 0x5A/0xff, alpha: 1)
fillColor.setFill()
you get this result.
Have fun coding.
You're currently drawing 'a'. You want to draw 'b'.
extension UIBezierPath {
convenience init(rightAngledTriangleInRect: CGRect, offset withOffset: CGFloat) {
self.init()
move(to: CGPoint(x: rect.minX, y: rect.minY))
addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
addLine(to: CGPoint(x: rect.minX + offset, y: rect.maxY))
addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
addLine(to: CGPoint(x: rect.minX, y: rect.minY))
close()
}
}
Then use by:
let path = UIBezierPath.init(rightAngledTriangleInRect: rect, withOffset: 20.0)
Note that there's no need to add self. in the convenience inits for the bezier path instructions.
If you're doing a lot of drawing, I'd recommend adding a number of these inits to make life easier.
To use in your current TriangleView:
class TriangleView: UIView {
override func draw(_ rect: CGRect) {
let path = UIBezierPath.init(rightAngledTriangleInRect: rect, withOffset: 20.0)
let shape = CAShapeLayer()
shape.path = path.cgPath
// Set the mask
layer.mask = shape
}
}
There are some easy way too for achivieng this but for using transform here is the sample code.use this transform instead of yours.it's sample code for having right angle triangle from your drawing:
triangle.transform = CGAffineTransform.identity.rotated(by: CGFloat.pi).translatedBy(x: (triangle.bounds.width / 2), y: 0.0)
you should notice that CGAffineTransform rotate the view from it's origins