dang
dang

Reputation: 2412

Add tooltip pointer in Swift iOS

On button click, I want to create tooltip. and now I am getting a rectangle.

How do I add a pointer as below image?

Expected: Expected

Current: Current

Following is the code where I am trying to create tooltip like view

var ToolTipView = UIView()
    func tooltip(){
        for view in self.ToolTipView.subviews {
        view.removeFromSuperview()
    }

    ToolTipView.removeFromSuperview()
    ToolTipView.frame = CGRect(x:50, y:self.view.bounds.height-150, width:100, height:50)
    ToolTipView.backgroundColor = UIColor(red:0, green:0, blue:0, alpha:0.7)
    ToolTipView.layer.cornerRadius = 5.0

    let txtLabel = UILabel(frame: CGRect(x:0, y:2, width:100, height:50))
    txtLabel.text = "Click"
    txtLabel.textAlignment = NSTextAlignment.Center
    txtLabel.textColor = UIColor(red:1, green:1, blue:1, alpha:1)
    txtLabel.font = UIFont(name:"HelveticaNeue", size: 16)
    self.ToolTipView.addSubview(txtLabel)
    self.view.addSubview(ToolTipView)
    self.view.bringSubviewToFront(ToolTipView)
}

Upvotes: 1

Views: 15782

Answers (3)

manoranjan nayak
manoranjan nayak

Reputation: 51

class MyViewController: UIViewController, UIPopoverPresentationControllerDelegate {

    @objc func expandClicked(sender: UIButton!) {
        let vc = UIViewController()
        vc.view.backgroundColor = .white
        display(viewcontroller: vc, size: CGSize(width: 150, height: 40), sender: sender)
    }

    func display(viewcontroller: UIViewController, size: CGSize, sender: UIView) {
        let controller = viewcontroller
        controller.modalPresentationStyle = .popover
        controller.popoverPresentationController?.delegate = self
        controller.preferredContentSize = size
        let presentationController = controller.popoverPresentationController!
        presentationController.sourceView = sender
        presentationController.sourceRect = sender.bounds
        presentationController.backgroundColor = .white
        let buttonPosition = CGPoint(x: sender.bounds.minX, y: sender.bounds.maxY)
        let p = sender.convert(buttonPosition, to: transactionTableView)
        if (transactionTableView.bounds.maxY - p.y) > 70 {
            presentationController.permittedArrowDirections = .up
        } else {
            presentationController.permittedArrowDirections = .down
        }
        self.present(controller, animated: true)
   }

   public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
        return UIModalPresentationStyle.none 
   }
}
    

Upvotes: 0

Callum
Callum

Reputation: 201

How about this?

extension UIView {
    func displayTooltip(_ message: String, completion: (() -> Void)? = nil) {
        let tooltipBottomPadding: CGFloat = 12
        let tooltipCornerRadius: CGFloat = 6
        let tooltipAlpha: CGFloat = 0.95
        let pointerBaseWidth: CGFloat = 14
        let pointerHeight: CGFloat = 8
        let padding = CGPoint(x: 18, y: 12)
        
        let tooltip = UIView()
        
        let tooltipLabel = UILabel()
        tooltipLabel.text = "    \(message)    "
        tooltipLabel.font = UIFont.systemFont(ofSize: 12)
        tooltipLabel.contentMode = .center
        tooltipLabel.textColor = .white
        tooltipLabel.layer.backgroundColor = UIColor(red: 44 / 255, green: 44 / 255, blue: 44 / 255, alpha: 1).cgColor
        tooltipLabel.layer.cornerRadius = tooltipCornerRadius
        
        tooltip.addSubview(tooltipLabel)
        tooltipLabel.translatesAutoresizingMaskIntoConstraints = false
        tooltipLabel.bottomAnchor.constraint(equalTo: tooltip.bottomAnchor, constant: -pointerHeight).isActive = true
        tooltipLabel.topAnchor.constraint(equalTo: tooltip.topAnchor).isActive = true
        tooltipLabel.leadingAnchor.constraint(equalTo: tooltip.leadingAnchor).isActive = true
        tooltipLabel.trailingAnchor.constraint(equalTo: tooltip.trailingAnchor).isActive = true
        
        let labelHeight = message.height(withWidth: .greatestFiniteMagnitude, font: UIFont.systemFont(ofSize: 12)) + padding.y
        let labelWidth = message.width(withHeight: .zero, font: UIFont.systemFont(ofSize: 12)) + padding.x
        
        let pointerTip = CGPoint(x: labelWidth / 2, y: labelHeight + pointerHeight)
        let pointerBaseLeft = CGPoint(x: labelWidth / 2 - pointerBaseWidth / 2, y: labelHeight)
        let pointerBaseRight = CGPoint(x: labelWidth / 2 + pointerBaseWidth / 2, y: labelHeight)
        
        let pointerPath = UIBezierPath()
        pointerPath.move(to: pointerBaseLeft)
        pointerPath.addLine(to: pointerTip)
        pointerPath.addLine(to: pointerBaseRight)
        pointerPath.close()
        
        let pointer = CAShapeLayer()
        pointer.path = pointerPath.cgPath
        pointer.fillColor = UIColor(red: 44 / 255, green: 44 / 255, blue: 44 / 255, alpha: 1).cgColor
        
        tooltip.layer.addSublayer(pointer)
        (superview ?? self).addSubview(tooltip)
        tooltip.translatesAutoresizingMaskIntoConstraints = false
        tooltip.bottomAnchor.constraint(equalTo: topAnchor, constant: -tooltipBottomPadding + pointerHeight).isActive = true
        tooltip.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        tooltip.heightAnchor.constraint(equalToConstant: labelHeight + pointerHeight).isActive = true
        tooltip.widthAnchor.constraint(equalToConstant: labelWidth).isActive = true
        
        tooltip.alpha = 0
        UIView.animate(withDuration: 0.2, animations: {
            tooltip.alpha = tooltipAlpha
        }, completion: { _ in
            UIView.animate(withDuration: 0.5, delay: 0.5, animations: {
                tooltip.alpha = 0
            }, completion: { _ in
                tooltip.removeFromSuperview()
                completion?()
            })
        })
    }
}

with String extensions:

extension String {
    func width(withHeight constrainedHeight: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: constrainedHeight)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
        return ceil(boundingBox.width)
    }
    
    func height(withWidth constrainedWidth: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: constrainedWidth, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
        return ceil(boundingBox.height)
    }
}

Usage:

clickMeButton.displayTooltip("Clicked!")

Result: Dark gray tooltip with text shown over button

Upvotes: 1

Saad
Saad

Reputation: 8947

Try CocoaControls for a list of libraries developed for you. Simply search your query and there will be a list of controls. Chose one of them, and select download source. It will take to Github, download or use pod for that control and enjoy :)

Here is link to your desired query as example.

Upvotes: 1

Related Questions