[SWIFT] Creating an InputAccesorryView for sending messages

Completed form

Place the text input field and send button made with xib in inputAccesorryView, When you enter text and press the button, the text is displayed in the TextView for display in the view controller.

Delegate is used to transfer text to the view controller.

スクリーンショット 2020-12-13 10.14.42.png

Introduction

Arrange TextView appropriately to display the text entered as NavigationController in Storyboard TextView has an Outlet connection to ViewController.swift with the name "displayTextView"

スクリーンショット 2020-12-08 20.46.35.png

1. Create xib file

Select New File ... anywhere in the project folder

スクリーンショット 2020-12-08 20.52.00.png

Select View and enter the file name to Create

スクリーンショット 2020-12-08 20.56.23.png

2. Create UI

Change the Size to FreeForm to make the form look like it. スクリーンショット 2020-12-08 21.10.27.png

Place TextView and Button to input message

Add Constraints with 10 constraints on the top, bottom and left of the TextView (The point is to specify the restrictions below from the Safe Area)

スクリーンショット 2020-12-08 21.20.12.png

Check the button's Vertically in Container to add a constraint

スクリーンショット 2020-12-08 21.27.40.png

Align the vertical center of TextView and Button

Change the vertical center constraint setting in the Button's Vertically in Container above as shown in the figure below. Align the center of the TextView with the center of the Button スクリーンショット 2020-12-08 21.37.47.png

Restrict the left and right and width of the button

スクリーンショット 2020-12-08 22.29.00.png

Uncheck Scrolling Enable in TextView

スクリーンショット 2020-12-09 22.26.41.png

This is the end of UI creation

3. Create UIView class for xib

Create a swift file and enter the following code

Temporarily create a UIView class so that it can be set to xib for the time being

MsgInputAccessoryView.swift


import UIKit

class MsgInputAccessoryView: UIView {
  
  override init(frame: CGRect) {
    super.init(frame: frame)
  }
 
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

Set UIView Class to File's Owner of xib

スクリーンショット 2020-12-09 21.58.37.png

Coding by connecting the UI to the created UIView Class

MsgInputAccessoryView.swift


import UIKit

//Delegate tells the input text to the view controller(:class is circular reference prevention)
protocol MsgInputAccessoryViewDelegate :class {
  func tapedSendButton(text: String)
  
}

class MsgInputAccessoryView: UIView {
  
  @IBOutlet weak var msgTextView: UITextView!
  @IBOutlet weak var sendButton: UIButton!
  
  //Called when sendButton is pressed
  @IBAction func tapedSendButton(_ sender: Any) {

    guard let text = msgTextView.text else { return }
    //Pass the value entered in delegate
    delegate?.tapedSendButton(text: text)
    
  }
  
  weak var delegate : MsgInputAccessoryViewDelegate?
  
  
  override init(frame: CGRect) {
    super.init(frame: frame)
    
    //Paste view
    nibInit()
    setupViews()
    //Setting to make the height of msgTextView variable ①
    autoresizingMask = .flexibleHeight
  }
  
  
  private func setupViews(){
    
    msgTextView.layer.cornerRadius = 15
    //Make sure you can't press it without input
    sendButton.isEnabled = false
    
    //Leave blank initially
    msgTextView.text = ""
    msgTextView.delegate = self

  }
  
  //Post-send processing(Executed from ViewController)
  func removeText(){
    
    msgTextView.text = ""
    sendButton.isEnabled = false
  }
  
  //Setting to make the height of msgTextView variable ②
  override var intrinsicContentSize: CGSize{
    return .zero
  }
  
  //xib load method
  private func nibInit(){
    
    let nib = UINib(nibName: "MsgInputAccessoryView", bundle: nil)
    //xib File's Associate class with Owner
    guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return }
    
    view.frame = self.bounds
    //Set so that the view size and position of the child can be adjusted automatically when the view size of the parent changes.
    view.autoresizingMask = [.flexibleHeight,.flexibleWidth]
    
    //It's just a UIView, so UINib.instantiate(self).Get from first and addSubview to ProfileView
    self.addSubview(view)
  }
  
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

extension MsgInputAccessoryView: UITextViewDelegate{
  //Called every time textView is edited
  func textViewDidChange(_ textView: UITextView) {
    if textView.text.isEmpty {
      //Disable sendButton if inputTextView is blank
      sendButton.isEnabled = false

    }else{
      sendButton.isEnabled = true
      
    }
  }
}

4. Coding the view controller to display the received text

Set to InputAccessoryView and display the text received by Delegate in TextView

ViewController.swift


import UIKit

class ViewController: UIViewController {

  @IBOutlet weak var displayTextView: UITextView!
  //Instantiate by giving frame size to view(Give a value to the property at the same time as the closure expression definition)
  //Add lazy so that you can access self with delegate
  private lazy var msgInputAccessoryView: MsgInputAccessoryView = {
    
    let view  = MsgInputAccessoryView()
    view.frame = .init(x: 0, y: 0, width: view.frame.width, height: 100)
    view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 100)
    //Delegate set in MsgInputAccessoryView
    view.delegate = self
    return view
    
  }()
  
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
  }
  
  
  //Set UIView to the original inputAccessoryView in viewController
  override var inputAccessoryView: UIView?{

   // get{
      return msgInputAccessoryView
    //}
  }

  //MsgInputAccessoryView is displayed from the beginning
  //ViewController(self)Rewrite canBecomeFirstResponder to true
  override var canBecomeFirstResponder: Bool{
    return true

  }
}

//Receive value with delegate from MsgInputAccessoryView
extension ViewController: MsgInputAccessoryViewDelegate{
  func tapedSendButton(text: String) {
    print(text)
    
    displayTextView.text = text
    
    //When the value is received, clear msgTextView
    msgInputAccessoryView.removeText()
    
  }
}

That's it

Finally

I would appreciate it if you could point out any mistakes.

Recommended Posts

Creating an InputAccesorryView for sending messages
A memorandum for creating an extended logger using org.slf4j.Logger
Procedure for publishing an application using AWS (4) Creating a database
Building an environment for creating apps with Rails and Vue