If you tap the button on the bar on the toolbar on the keyboard displayed by UITextField or UITextView, another bar will be displayed.
As an image, the toolbar for selecting the reminder time and locale, which is implemented in the genuine iOS reminder app, will be the same.
I often see the basic way to set a toolbar on the keyboard with textView.inputAccessoryView = toolbar
,
I couldn't find a way to change the height of the bar dynamically, so I hope it helps.
When you tap the bell button on the toolbar, a button for selecting the notification time interval will appear one step higher.
At first, I tried to dynamically change the height of the toolbar set with textView.inputAccessoryView = toolbar
, but I couldn't do it well. .. ..
(Please let me know if you know how to implement it!)
This time, for the toolbar set with textView.inputAccessoryView = toolbar
, hide the child view with addSubview
and sendSubviewToBack
on the back side.
The display is switched by moving up and down.
This time, I created a CustomTableViewCell in the UITableView and placed the UITextView on the cell. Makes the keyboard visible to the UITextView in the CustomTableViewCell to display the toolbar on the keyboard.
CustomTableViewCell.swift
let lowerToolbar = LowerToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
lowerToolbar.initToolbarButton(item: item!)
lowerToolbar.delegate = self
textView.inputAccessoryView = lowerToolbar
Here is the contents of the lowerToolbar set above
LowerToolbar.swift
protocol LowerToolbarDelegete: class {
func onTouchToolbarButton(selectedTimeInterval: Int)
}
class LowerToolbar: UIView {
@IBOutlet weak var toolbar: UIToolbar!
var upperToolbar: UIView?
var isHidenUpperToolbar: Bool = true
var upperToolbarCenterY: CGFloat = 0
weak var delegate: LowerToolbarDelegete! = nil
let oneHourButton = UIButton(type: .system)
let threeHourButton = UIButton(type: .system)
let fiveHourButton = UIButton(type: .system)
var item: ItemModel?
var selectedTimeInterval: Int = 0 // Last tapped button.
override init(frame: CGRect){
super.init(frame: frame)
loadNib()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
loadNib()
}
func loadNib(){
let view = Bundle.main.loadNibNamed("LowerToolbar", owner: self, options: nil)?.first as! UIView
view.frame = self.bounds
self.addSubview(view)
toolbar.clipsToBounds = true
upperToolbar = UIView(frame: CGRect(x: 0, y: -1, width: self.frame.size.width, height: 61))
upperToolbar?.backgroundColor = UIColor.toolbar
self.addSubview(upperToolbar!)
self.sendSubviewToBack(upperToolbar!)
upperToolbarCenterY = upperToolbar!.center.y
}
func createToolbarButton(btn: UIButton, title: String, timeInterval: Int) {
btn.setTitle(title, for: .normal)
btn.tag = timeInterval
btn.layer.borderWidth = 1
btn.layer.borderColor = UIColor.toolbarBorder.cgColor
btn.layer.cornerRadius = 20
btn.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
btn.backgroundColor = UIColor.toolbarButton
btn.addTarget(self, action: #selector(tapHoursButton), for: .touchUpInside)
upperToolbar?.addSubview(btn)
if self.item?.timeInterval == timeInterval {
btn.layer.borderColor = UIColor.systemBlue.cgColor
btn.backgroundColor = UIColor.systemBlue
btn.tintColor = UIColor.white
}
}
func initToolbarButton(item: ItemModel) {
self.item = item
createToolbarButton(btn: oneHourButton, title: "1hour", timeInterval: 60)
oneHourButton.translatesAutoresizingMaskIntoConstraints = false
oneHourButton.leadingAnchor.constraint(equalTo: upperToolbar!.leadingAnchor, constant: 20).isActive = true
oneHourButton.topAnchor.constraint(equalTo: upperToolbar!.topAnchor, constant: 10).isActive = true
createToolbarButton(btn: threeHourButton, title: "3hour", timeInterval: 180)
threeHourButton.translatesAutoresizingMaskIntoConstraints = false
threeHourButton.leadingAnchor.constraint(equalTo: oneHourButton.trailingAnchor, constant: 20).isActive = true
threeHourButton.topAnchor.constraint(equalTo: upperToolbar!.topAnchor, constant: 10).isActive = true
createToolbarButton(btn: fiveHourButton, title: "5hour", timeInterval: 400)
fiveHourButton.translatesAutoresizingMaskIntoConstraints = false
fiveHourButton.leadingAnchor.constraint(equalTo: threeHourButton.trailingAnchor, constant: 20).isActive = true
fiveHourButton.topAnchor.constraint(equalTo: upperToolbar!.topAnchor, constant: 10).isActive = true
}
func initUpperToolbar() {
upperToolbar?.center.y = upperToolbarCenterY
isHidenUpperToolbar = true
}
func decorateTappedHourButton(btn: UIButton) {
btn.layer.borderColor = UIColor.systemBlue.cgColor
btn.backgroundColor = UIColor.systemBlue
btn.tintColor = UIColor.white
}
func decorateNormalHourButton(btn: UIButton) {
btn.layer.borderColor = UIColor.toolbarBorder.cgColor
btn.backgroundColor = UIColor.toolbarButton
btn.tintColor = .systemBlue
}
func decorateHourButton(btn1: UIButton, btn2: UIButton, btn3: UIButton, newTimeInterval: Int) {
if selectedTimeInterval == newTimeInterval {
selectedTimeInterval = 0
decorateNormalHourButton(btn: btn1)
} else {
selectedTimeInterval = newTimeInterval
decorateTappedHourButton(btn: btn1)
}
decorateNormalHourButton(btn: btn2)
decorateNormalHourButton(btn: btn3)
}
@objc func tapHoursButton(btn: UIButton) {
switch btn.tag {
case 60:
decorateHourButton(btn1: oneHourButton, btn2: threeHourButton, btn3: fiveHourButton, newTimeInterval: btn.tag)
case 180:
decorateHourButton(btn1: threeHourButton, btn2: oneHourButton, btn3: fiveHourButton, newTimeInterval: btn.tag)
case 400:
decorateHourButton(btn1: fiveHourButton, btn2: oneHourButton, btn3: threeHourButton, newTimeInterval: btn.tag)
default:
print("no item")
}
delegate?.onTouchToolbarButton(selectedTimeInterval: btn.tag)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if isHidenUpperToolbar {
let rect = self.bounds
return rect.contains(point)
} else {
var rect = self.bounds
if rect.contains(point) {
return rect.contains(point)
}
rect.origin.y -= 60
return rect.contains(point)
}
}
@IBAction func tapBellButton(_ sender: Any) {
if isHidenUpperToolbar {
UIView.animate(withDuration: 0.1, animations: {
self.upperToolbar!.center.y -= 60
self.isHidenUpperToolbar = false
self.layoutIfNeeded()
})
} else {
UIView.animate(withDuration: 0.1, animations: {
self.upperToolbar!.center.y += 60
self.isHidenUpperToolbar = true
self.layoutIfNeeded()
})
}
}
}
LowerToolbar.swift
func loadNib(){
let view = Bundle.main.loadNibNamed("LowerToolbar", owner: self, options: nil)?.first as! UIView
view.frame = self.bounds
self.addSubview(view)
toolbar.clipsToBounds = true
upperToolbar = UIView(frame: CGRect(x: 0, y: -1, width: self.frame.size.width, height: 61))
upperToolbar?.backgroundColor = UIColor.toolbar
self.addSubview(upperToolbar!)
self.sendSubviewToBack(upperToolbar!)
upperToolbarCenterY = upperToolbar!.center.y
}
When initializing the LowerToolbar, create an UpperToolbar and hide it by addSubview
and sendSubviewToBack
to move it to the back.
LowerToolbar.swift
@IBAction func tapBellButton(_ sender: Any) {
if isHidenUpperToolbar {
UIView.animate(withDuration: 0.1, animations: {
self.upperToolbar!.center.y -= 60
self.isHidenUpperToolbar = false
self.layoutIfNeeded()
})
} else {
UIView.animate(withDuration: 0.1, animations: {
self.upperToolbar!.center.y += 60
self.isHidenUpperToolbar = true
self.layoutIfNeeded()
})
}
}
When you tap the bell button, the upper Toolbar is moved up and down by 60.
LowerToolbar.swift
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if isHidenUpperToolbar {
//upperToolbar is hidden
//When lowerToolbar is tapped
let rect = self.bounds
return rect.contains(point)
} else {
//Although the upper Toolbar is displayed
//When upperToolbar is tapped
var rect = self.bounds
if rect.contains(point) {
return rect.contains(point)
}
//When lowerToolbar is tapped
rect.origin.y -= 60
return rect.contains(point)
}
}
The point is this part, because if you move the upperToolbar up to 60, it will extend beyond the frame area of the lowerToolbar of the parent view.
I will not be able to receive the event of the button on the upperToolbar.
So, override override func point (inside point: CGPoint, with event: UIEvent?)-> Bool
I am trying to receive an event when the upperToolbar is tapped.
This time, I generated an upperToolbar in the lowerToolbar and created it with code. This is when I used the upperToolbar created individually in the storyboard, This is because the event tapped using delegate could not be delegated to upperToolbar-> lowerToolbar-> CustomTableViewCell. (I couldn't set delegate for upperToolbar with lowerToolbar.)
I think it would be the simplest implementation if the height of the toolbar could be changed dynamically. I couldn't change the height as I expected, so I tried a different method. How do you implement it in Apple's genuine reminder app? I'm curious. I hope you find it helpful.
Recommended Posts