[SWIFT] Display a balloon message in BarButtonItem of NavigationBar with a size according to the amount of text.

Introduction

I want a balloon that can easily introduce the function of the button when the application starts, Create a balloon message that can be popped from the navigation bar button.

Completed form スクリーンショット 2021-01-16 15.37.06.png

Preparation

スクリーンショット 2021-01-16 17.07.04.png

Add NavigationController in MainStoryBoard. To add, select View Controller and select "Editor"-> "Enbed In"-> "Navigation Controller".

code

Since the triangular arrow part of the balloon view is added later, the completed view will be larger than the specified size. In the case of the upper arrow like this time, if you place the contents part at the zero point of the view Parts will be placed on the arrow. To prevent that, get the safe area of ​​the view controller of the balloon and place the parts.

ViewContoller.swift


import UIKit

class ViewController: UIViewController {
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    
    //Creating a button
    let actionButton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(actionTapped(sender:)))
    
    //Add right button to navigation bar (when adding two or more)
    navigationItem.setRightBarButtonItems([editButtonItem,actionButton], animated: true)
    
    //If only one
    //navigationItem.rightBarButtonItem = actionButton
    
  }
  
  override func viewDidAppear(_ animated: Bool) {
    super .viewDidAppear(animated)
    
    //Execute pop display method
    showPopGuide()
    
  }
  
  //Pop display method for guide balloons
  func showPopGuide() {

    //Specify the target to display the callout
    if let target = navigationItem.rightBarButtonItems?[1] {
      
      
      let popVC = PopoverViewController()
      popVC.modalPresentationStyle = .popover
      //pop Specify the size of the View to be displayed (this time, it is updated according to the amount of characters)
      popVC.preferredContentSize = CGSize(width: 100, height: 100)
      //Specify the barButton for which you want to display the arrow
      popVC.popoverPresentationController?.barButtonItem = target
      //Limit the direction of the arrow
      popVC.popoverPresentationController?.permittedArrowDirections = .any
      //Delegate settings
      popVC.popoverPresentationController?.delegate = self
      
      popVC.text = "Pop the message.\The size of nView is the amount of text\n Automatically adjusted."
      
      //Show balloon
      present(popVC, animated: true, completion: nil)
      
    }
  }
  
  
  //Method executed when actionButton is tapped
  @objc func actionTapped(sender:UIBarButtonItem){
    
    print("ActionTapped")
  }
}


// MARK: - UIPopoverPresentationControllerDelegate

extension ViewController: UIPopoverPresentationControllerDelegate {
  
  //By returning none, popover can be displayed on iPhone as well.
  func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
    return .none
  }
  
  //Specify whether to close when tapping outside the popover
  func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool {
    //If true is returned, it will close when tapping outside the view
    return true
  }
}


// MARK: -PopoverViewController (ViewController to pop display)

//ViewController for pop
class PopoverViewController: UIViewController{
  
  //TextView to display
  let guideTextView = UITextView()
  
  var text:String = ""
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .cyan
    
    //textView settings
    guideTextView.isEditable = false
    guideTextView.isSelectable = false
    guideTextView.backgroundColor = .clear
    guideTextView.text = text
    view.addSubview(guideTextView)
    
    //Set to receive that this view controller itself has been tapped
    view.isUserInteractionEnabled = true
    //Specify the method to execute when this view controller is tapped (used to close the VC)
    view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(viewTapped(sender:))))
    
    
  }
  
  
  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    //iOS 13 or later Use Safe Area to exclude the arrow area
    //viewWillLayoutSubviews will be available after the safeArea is confirmed()Or later
    let safeArea = view.safeAreaInsets
    
    //Set the size of textView
    guideTextView.frame.size = preferredContentSize
    guideTextView.frame.origin = CGPoint(x:safeArea.left , y: safeArea.top)

  }
  
  
  //Fit the size to the contents of the textView(Override and rewrite ViewController properties)
  override var preferredContentSize: CGSize {
    get {
      if presentingViewController != nil {
        return guideTextView.sizeThatFits(presentingViewController!.view.bounds.size)
      } else {
        return super.preferredContentSize
      }
    }set {
      super.preferredContentSize = newValue
    }
  }
  
  
  //Method called when tapping this View itself
  @objc func viewTapped(sender:UITapGestureRecognizer){
    
    print("viewtapped")
    //Close the view itself
    dismiss(animated: true, completion: nil)
  }
}


Recommended Posts

Display a balloon message in BarButtonItem of NavigationBar with a size according to the amount of text.
How to display the text entered in text_area in Rails with line breaks
How to get the ID of a user authenticated with Firebase in Swift
[Rails] I want to display the link destination of link_to in a separate tab
[jsoup] How to get the full amount of a document
Find the number of days in a month with Kotlin
Try to imitate the idea of a two-dimensional array with a one-dimensional array
I made a gem to post the text of org-mode to qiita
Replace with a value according to the match with a Java regular expression
Let's create a TODO application in Java 5 Switch the display of TODO
The story of forgetting to close a file in Java and failing
How to check before sending a message to the server with Spring Integration
I want to display a PDF in Chinese (Korean) with thin reports
How to change the value of a variable at a breakpoint in intelliJ
How to get the absolute path of a directory running in Java
[Ruby] Returns characters in a pyramid shape according to the entered numbers
Display text on top of the image
Ability to display a list of products
Use hashes well in Ruby to calculate the total amount of an order
Let's make a calculator application with Java ~ Create a display area in the window
How to display the amount of disk used by Docker container for each container
[Rails] How to get rid of flash messages in a certain amount of time
How to make a unique combination of data in the rails intermediate table
Validate the identity token of a user authenticated with AWS Cognito in Java
Make a margin to the left of the TextField
How to display a web page in Java
Set the time of LocalDateTime to a specific time
[Ruby] Code to display the day of the week
How to display the result of form input
What to do if you can't get the text of an element in Selenium
[Java] Integer information of characters in a text file acquired by the read () method
How to use git with the power of jgit in an environment without git commands
How to save a file with the specified extension under the directory specified in Java to the list
Write a test by implementing the story of Mr. Nabeats in the world with ruby
[Docker] How to see the contents of Volumes. Start a container with root privileges.
Sample code to assign a value in a property file to a field of the expected type
Let's implement a function to limit the number of access to the API with SpringBoot + Redis
[Java] How to display the day of the week acquired by LocalDate, DateTimeformatter in Japanese
Summary of how to use the proxy set in IE when connecting with Java
[Rails] How to display the weather forecast of the registered address in Japanese using OpenWeatherMap