Create Tooltips in Swift

Tejanvm
5 min readJan 3, 2020

Let’s create tooltip views that can be used in our iOS apps

In this tutorial, we will not only create a tooltip class but we will also create a simple app that showcases the usage of tooltips.

Our end result will look something like this:

We will create a button called “Show Tooltip” and when it is clicked, we will show the tooltip right above the button.

Let’s start by creating a project and name it as ToolTips.

Press cmd + N to create a new file. Select the ‘Cocoa Touch class’

and click ‘Next’ and enter the name of the file as ‘ToolTipView’ and make it a subclass of UIView class.

In the ToolTipView class, let us create an enum that represents the position of the tooltip. In this example, we will have 3 positions defined for tooltip.

enum ToolTipPosition: Int {     case left     case right     case middle }

We now have our enum and let’s continue ahead by creating few properties for our tooltip.

var roundRect:CGRect!let toolTipWidth : CGFloat = 20.0let toolTipHeight : CGFloat = 12.0let tipOffset : CGFloat = 20.0var tipPosition : ToolTipPosition = .middle
  1. The var roundRect will store the reference to the actual CGRect that has the rounded rect bezier path that acts as the background for the text content.
  2. The var toolTipWidth and toolTipHeight are straightforward that references the tool tip width and height.
  3. We don’t want our tooltip to appear at the very beginning or very end of the view. Hence we need to give some offset so that tip alway be within the roundRect and look nice.
  4. By default, let us keep the tip position in the middle.

At this point, we have created the class with some vars. We now need to create a custom init method for our custom UIView class.

convenience init(frame: CGRect, text : String, tipPos: ToolTipPosition){    self.init(frame: frame)    self.tipPosition = tipPos}

We have the method that init the required vars. It is now time to do some drawing. If you are not familiar with the bezier-paths, do not afraid. They are not as scary as you think.

For our tooltip, we need two bezier paths. One is the top rounded rectangle path. The second one is the bottom triangle path. For the first one,

roundRect = CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: rect.height — toolTipHeight)let roundRectBez = UIBezierPath(roundedRect: roundRect, cornerRadius: 5.0)

For the triangle path:

func createTipPath() -> UIBezierPath{   let tooltipRect = CGRect(x: getX(), y: roundRect.maxY, width: toolTipWidth, height: toolTipHeight)   let trianglePath = UIBezierPath()   trianglePath.move(to: CGPoint(x: tooltipRect.minX, y: tooltipRect.minY))   trianglePath.addLine(to: CGPoint(x: tooltipRect.maxX, y: tooltipRect.minY))   trianglePath.addLine(to: CGPoint(x: tooltipRect.midX, y: tooltipRect.maxY))   trianglePath.addLine(to: CGPoint(x: tooltipRect.minX, y: tooltipRect.minY))   trianglePath.close()   return trianglePath}

Now we need to combine these two paths into one. I created a method that does it.

func drawToolTip(_ rect : CGRect){   roundRect = CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: rect.height — toolTipHeight)   let roundRectBez = UIBezierPath(roundedRect: roundRect, cornerRadius: 5.0)   let trianglePath = createTipPath()   roundRectBez.append(trianglePath)   let shape = createShapeLayer(roundRectBez.cgPath)   self.layer.insertSublayer(shape, at: 0)}func createShapeLayer(_ path : CGPath) -> CAShapeLayer{   let shape = CAShapeLayer()   shape.path = path   shape.fillColor = UIColor.darkGray.cgColor   shape.shadowColor = UIColor.black.withAlphaComponent(0.60).cgColor   shape.shadowOffset = CGSize(width: 0, height: 2)   shape.shadowRadius = 5.0   shape.shadowOpacity = 0.8   return shape}

Every UIView class has a drawRect: method that we can use to perform the drawing related operations.

override func draw(_ rect: CGRect) {   super.draw(rect)   drawToolTip(rect)}

That’s it. We have created a custom class for our tooltip. It is time to check how this looks like. Let’s go back to our ViewController class, and add the following code:

@IBOutlet weak var showToolTipBtn : UIButton!@IBAction func showToolTipClicked(){   showToolTip()}func showToolTip() {   let p = showToolTipBtn.center   let tipWidth: CGFloat = 200   let tipHeight: CGFloat = 80   let tipX = p.x — tipWidth / 2   let tipY: CGFloat = p.y — 80   let tipView = ToolTipView(frame: CGRect(x: tipX, y: tipY, width: tipWidth, height: tipHeight), text: “Hello User! This is a sample tool tip”, tipPos: .middle)   UIApplication.shared.keyWindow?.addSubview(tipView)   performShow(tipView)}func performShow(_ v: UIView?) {   v?.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)   UIView.animate(withDuration: 0.3, delay: 0.3, options: .curveEaseOut, animations: {     v?.transform = .identity   }) { finished in     // do something once the animation finishes, put it here   }}

Make sure you connect the outlets and IBActions in the storyboard. When you run the code and after you click the button, it should look something like this:

Wait, that looks like a tooltip view but where is the text in it? Yes , you caught me. Let’s go back to our custom class view and add this code.

func createLabel(_ text : String){   let label = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height — toolTipHeight))   label.text = text   label.textColor = .white   label.textAlignment = .center   label.numberOfLines = 0   label.lineBreakMode = .byWordWrapping   addSubview(label)}

and update our convenience init

convenience init(frame: CGRect, text : String, tipPos: ToolTipPosition){   self.init(frame: frame)   self.tipPosition = tipPos   createLabel(text)}

Run the project again, and you will now see the text inside our tip view.

--

--

Tejanvm

I'm an iOS Developer. I love to learn new things in programming and share my knowledge.