I'm trying to create a drop-down menu. In this drop-down menu, I have a UIView which the user taps on to drop down a UITableView. In the process of creating it, I tried to change the separator color to clear. However, it's not working. I have tried settings it different colors all to no avail, but for some reason when I try to set its edge inset it works. Can someone help me out?
private var selectedBackgroundColor : UIColor = UIColor(red: 131/255, green: 43/255, blue: 205/255, alpha: 1.0)
private var unSelectedBackgroundColor : UIColor = UIColor(red: 178/255, green: 90/255, blue: 253/255, alpha: 1.0)
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInitializer()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.commonInitializer()
}
private func commonInitializer() {
self.backgroundColor = self.unSelectedBackgroundColor
self.layer.cornerRadius = self.bounds.height/2
let shadowColor = UIColor(red: 0xC8, green: 0xC6, blue: 0xC6)
self.layer.applySketchShadow(color: shadowColor, alpha: 0.5, x: 0, y: 2, blur: 5, spread: 0)
self.itemTableView.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
self.itemTableView.translatesAutoresizingMaskIntoConstraints = false
self.itemTableView.delegate = self
self.itemTableView.dataSource = self
self.itemTableView.showsVerticalScrollIndicator = false
self.itemTableView.separatorColor = UIColor.clear
self.itemTableView.backgroundColor = self.unSelectedBackgroundColor
}
override func didMoveToSuperview() {
self.superview?.addSubview(itemTableView)
self.superview?.bringSubviewToFront(itemTableView)
//Drop down table view constraints
self.itemTableView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
self.itemTableView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
self.itemTableView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
self.tableViewHeight = self.itemTableView.heightAnchor.constraint(equalToConstant: 0)
}
/**
Changes the drop down button view and table view to the selected color
*/
private func changeToSelectedBackgroundColor() {
self.backgroundColor = self.selectedBackgroundColor
self.itemTableView.backgroundColor = self.selectedBackgroundColor
}
/**
Changes only the drop down button view to the unselected color. Changes the table view to white
*/
private func changeToUnSelectedBackgroundColor() {
self.backgroundColor = self.unSelectedBackgroundColor
self.itemTableView.backgroundColor = UIColor.white
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = self.items[indexPath.row]
cell.backgroundColor = UIColor.clear
cell.textLabel?.textColor = UIColor.white
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.itemTableView.deselectRow(at: indexPath, animated: true)
self.retractDropDownList()
}
#objc func buttonTapped() {
self.flashLabel()
if isExtended {
self.retractDropDownList()
} else {
self.expandDropDownList()
}
}
private func expandDropDownList() {
isExtended = true
self.delegate?.dropDownViewExtended()
NSLayoutConstraint.deactivate([self.tableViewHeight])
self.tableViewHeight.constant = 150.0
NSLayoutConstraint.activate([
self.tableViewHeight])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.changeToSelectedBackgroundColor()
self.itemTableView.layoutIfNeeded()
self.layer.cornerRadius = 0.0
self.itemTableView.center.y += self.itemTableView.frame.height/2
}, completion: nil)
}
private func retractDropDownList() {
isExtended = false
self.delegate?.dropDownViewRetracted()
NSLayoutConstraint.deactivate([self.tableViewHeight])
self.tableViewHeight.constant = 0.0
NSLayoutConstraint.activate([
self.tableViewHeight])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.changeToUnSelectedBackgroundColor()
self.itemTableView.center.y -= self.itemTableView.frame.height/2
self.itemTableView.layoutIfNeeded()
self.layer.cornerRadius = self.bounds.height/2
}, completion: nil)
}
}
Related
I have the following structure UIViewController -> UIView -> UIView -> UITableView (it's a dropdown menu), and the method didSelectRowAt is not being called, but if I skip the first view (that after VC), everything working perfectly. I saw a similar question here where was stated that you should use the method bringSubviewToFront but I tried that and it's not working (maybe I did it wrong).
Here is my code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let someView = someView()
someView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(someView)
}
}
class someView: UIView {
public lazy var button: DropDownButton = {
let button = DropDownButton(frame: .init(x: 0, y: 0, width: 100, height: 40))
button.setTitle("Colors", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.dropView.dropDownOptions = ["1","2","1","2"]
button.dropView.delegate = self
return button
}()
init() {
super.init(frame: .init(x: 0, y: 0, width: 300, height: 100))
addSubview(button)
addSubview(scrollButtons)
setUpConstraints()
}
}
class DropDownButton: UIButton {
public var dropView = DropDownView()
private var height = NSLayoutConstraint()
private var isOpen = false
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
dropView = DropDownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
dropView.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropView)
self.superview?.bringSubviewToFront(dropView)
dropView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = dropView.heightAnchor.constraint(equalToConstant: 0)
}
func dismissDropDown() {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(
withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5,
options: .curveEaseInOut,
animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([self.height])
self.height.constant = self.dropView.tableView.contentSize.height
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.3,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5,
options: .curveEaseInOut,
animations: {
self.dropView.layoutIfNeeded()
self.dropView.center.y += self.dropView.frame.height / 2
}, completion: nil)
} else {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(
withDuration: 0.3,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5,
options: .curveEaseInOut,
animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
}
}
class DropDownView: UIView {
var dropDownOptions = [String]()
var tableView = UITableView()
weak var delegate: DropDownDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
tableView.backgroundColor = UIColor.darkGray
self.backgroundColor = UIColor.darkGray
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.leftAnchor.constraint(equalTo: self.leftAnchor),
tableView.rightAnchor.constraint(equalTo: self.rightAnchor),
tableView.topAnchor.constraint(equalTo: self.topAnchor),
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension DropDownView: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dropDownOptions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = dropDownOptions[indexPath.row]
cell.backgroundColor = UIColor.darkGray
return cell
}
}
extension DropDownView: UITableViewDelegate {
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
print("cell pressed")
self.delegate?.dropDownPressed(string: dropDownOptions[indexPath.row])
self.tableView.deselectRow(at: indexPath, animated: true)
}
}
anyone please help me, I have a drop down but the I can't select it the second row. I can't figure out why, I try playing with bringSubviewsToFront or back. I try using layer.zPosition it still can't select the second row, but when I choose the first row it work. is it my auto layout setup incorrectly?
here is my ui and code setup
// This is my custom Button
class GDropdownSchedule: UIButton {
let headerLbl = GTitleLabel(name: "Schedule Type".localized(), fontSize: 13, color: #colorLiteral(red: 0.4588235294, green: 0.4941176471, blue: 0.5647058824, alpha: 1))
let bodyLbl = GSubtitleLabel(name: "Additional Note".localized(), fontSize: 16, color: #colorLiteral(red: 0.09803921569, green: 0.09803921569, blue: 0.09803921569, alpha: 1))
let dropDownIV = GIconImageView(img: #imageLiteral(resourceName: "down-chevron"))
var isOpen = false
let dropView = DropDownView()
var delegate: AddScheduleVCDelegate?
var height: NSLayoutConstraint!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configure() {
backgroundColor = #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1)
layer.cornerRadius = 5
layer.borderWidth = 1
layer.borderColor = #colorLiteral(red: 0.9411764706, green: 0.9411764706, blue: 0.9450980392, alpha: 1)
dropView.completion = { text in
self.bodyLbl.text = text
self.bodyLbl.font = UIFont(name: "NunitoSans-Regular", size: 12)
self.delegate?.toggleHide(selected: text)
self.dismissDropDown()
}
}
override func didMoveToSuperview() {
addSubview(headerLbl)
headerLbl.anchor(top: topAnchor, trailing: nil, bottom: nil, leading: leadingAnchor, topPadding: 10, rightPadding: 0, bottomPadding: 0, leftPadding: 10, width: 70, height: 18)
addSubview(bodyLbl)
bodyLbl.anchor(top: headerLbl.bottomAnchor, trailing: nil, bottom: bottomAnchor, leading: leadingAnchor, topPadding: 2, rightPadding: 10, bottomPadding: 10, leftPadding: 10, width: 0, height: 0)
addSubview(dropDownIV)
dropDownIV.tintColor = #colorLiteral(red: 0.2549019608, green: 0.3019607843, blue: 0.3568627451, alpha: 1)
dropDownIV.anchor(top: nil, trailing: trailingAnchor, bottom: bottomAnchor, leading: nil, topPadding: 0, rightPadding: 18, bottomPadding: 17, leftPadding: 0, width: 12, height: 10)
addSubview(dropView)
dropView.translatesAutoresizingMaskIntoConstraints = false
dropView.layer.zPosition = 1
height = dropView.heightAnchor.constraint(equalToConstant: 0)
NSLayoutConstraint.activate([
dropView.topAnchor.constraint(equalTo: headerLbl.bottomAnchor),
dropView.leadingAnchor.constraint(equalTo: leadingAnchor),
dropView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([height])
if self.dropView.tableView.contentSize.height > 150 {
height.constant = 150
} else {
height.constant = dropView.tableView.contentSize.height
}
NSLayoutConstraint.activate([height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.layoutIfNeeded()
self.dropView.center.y += self.dropView.frame.height / 2
})
} else {
isOpen = false
NSLayoutConstraint.deactivate([height])
height.constant = 0
NSLayoutConstraint.activate([height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
})
}
}
func dismissDropDown() {
isOpen = false
NSLayoutConstraint.deactivate([height])
height.constant = 0
NSLayoutConstraint.activate([height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
})
}
}
class DropDownView: UIView, UITableViewDelegate, UITableViewDataSource {
let tableView = UITableView()
var options = [String]()
var completion: ((String) -> Void)?
var isHideSchedule = false
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configure() {
translatesAutoresizingMaskIntoConstraints = false
addSubview(tableView)
tableView.backgroundColor = #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1)
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
tableView.anchor(top: topAnchor, trailing: trailingAnchor, bottom: bottomAnchor, leading: leadingAnchor, topPadding: 0, rightPadding: 0, bottomPadding: 0, leftPadding: 0, width: 0, height: 0)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return options.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = options[indexPath.row]
cell.textLabel?.font = UIFont(name: "NunitoSans-Regular", size: 12)
cell.textLabel?.textColor = #colorLiteral(red: 0.09803921569, green: 0.09803921569, blue: 0.09803921569, alpha: 1)
cell.backgroundColor = #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
completion?(options[indexPath.row])
tableView.deselectRow(at: indexPath, animated: true)
}
}
// This is in my viewController, the chooseScheduleDropDown is my customButton
[chooseScheduleDropDown, entryView, chooseDateView, chooseClass, startTimeView, endTimeView, descriptionView, saveBtn].forEach {
v in
v.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(v)
}
scrollView.insertSubview(entryView, belowSubview: chooseScheduleDropDown)
Because of the hit-test mechanism
You add the dropView on the Button GDropdownSchedule
The layout is
addSubview(dropView)
dropView.translatesAutoresizingMaskIntoConstraints = false
dropView.layer.zPosition = 1
height = dropView.heightAnchor.constraint(equalToConstant: 0)
NSLayoutConstraint.activate([
dropView.topAnchor.constraint(equalTo: headerLbl.bottomAnchor),
dropView.leadingAnchor.constraint(equalTo: leadingAnchor),
dropView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
From the looking and the existing code,
dropView's frame is out of the Button GDropdownSchedule's bounds Partly.
So u can see it , and your clicking does not work.
To override the hit-test mechanism is OK
class GDropdownSchedule: UIButton {
// ...
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// if the button is hidden/disabled/transparent it can't be hit
if self.isHidden || !self.isUserInteractionEnabled || self.alpha < 0.01 { return nil }
let dropViewF = dropView.frame
var index = 9
if bounds.contains(point){
index = 0
}
if dropViewF.contains(point){
index = 1
}
switch index {
case 0:
for subV in subviews.reversed(){
let realPoint = subV.convert(point, from: self)
let hit = subV.hitTest(realPoint, with: event)
if let v = hit{
return v
}
}
return self
case 1:
if dropView.alpha > 0.01{
let realPoint = dropView.convert(point, from: self)
let hit = dropView.hitTest(realPoint, with: event)
if let v = hit{
return v
}
}
default:
()
}
return nil
}
}
From Apple's Doc
hitTest(_:with:)
This method traverses the view hierarchy by calling the
point(inside:with:) method of each subview to determine which subview
should receive a touch event.
If point(inside:with:) returns true,
then the subview’s hierarchy is similarly traversed until the
frontmost view containing the specified point is found. If a view does
not contain the point, its branch of the view hierarchy is ignored.
You rarely need to call this method yourself, but you might override
it to hide touch events from subviews.
I am trying to make a button open up a dropdown menu styled UIView inside of a custom UITableViewCell. And it does open the UIView containing the other buttons. Great. However, the only clickable part is the tiny little bit of "1" inside the UITableViewCell. The rest of the dropdown menu is not clickable and you can click through it to the cell below. How can I make the button in the custom UITableViewCell open up the UIView with the dropdown menu and have each button inside the UIView clickable?
Here is what it's looking like now.
//The button inside the UITableViewCell:
class PriorityButton: UIButton, DropDownDelegate {
var dropDownView = DropDownView()
var height = NSLayoutConstraint()
var isOpen = false
override init(frame: CGRect) {
super.init(frame: frame)
dropDownView = DropDownView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
dropDownView.delegate = self
dropDownView.translatesAutoresizingMaskIntoConstraints = false
// button.setImage(UIImage(named: "UnChecked"), for: .normal)
// button.setImage(UIImage(named: "Checked"), for: .selected)
self.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
self.setTitleColor(.black, for: .normal)
self.backgroundColor = UIColor.white
self.layer.borderWidth = 2
self.layer.borderColor = UIColor.black.cgColor
self.titleLabel?.font = UIFont.init(name: "Avenir Next", size: 24)
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropDownView)
self.superview?.bringSubviewToFront(dropDownView)
dropDownView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
dropDownView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropDownView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = dropDownView.heightAnchor.constraint(equalToConstant: 0)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([self.height])
if self.dropDownView.priorityTableView.contentSize.height > 300 {
self.height.constant = 300
} else {
self.height.constant = self.dropDownView.priorityTableView.contentSize.height
}
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropDownView.layoutIfNeeded()
self.dropDownView.center.y += self.dropDownView.frame.height / 2
}, completion: nil)
} else {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropDownView.center.y -= self.dropDownView.frame.height / 2
self.dropDownView.layoutIfNeeded()
}, completion: nil)
}
}
func dismissDropDown() {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropDownView.center.y -= self.dropDownView.frame.height / 2
self.dropDownView.layoutIfNeeded()
}, completion: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func dropDownPressed(string: String) {
self.setTitle(string, for: .normal)
self.dismissDropDown()
}
}
// The Drop Down Menu view
class DropDownView: UIView, UITableViewDelegate, UITableViewDataSource {
let priorityLevel = ["1", "2", "3", "4", "5"]
var priorityTableView = UITableView()
var delegate: DropDownDelegate!
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .white
priorityTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellID")
priorityTableView.backgroundColor = .white
priorityTableView.delegate = self
priorityTableView.dataSource = self
priorityTableView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(priorityTableView)
priorityTableView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
priorityTableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
priorityTableView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
priorityTableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return priorityLevel.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath)
cell.textLabel?.text = priorityLevel[indexPath.row]
cell.backgroundColor = .white
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("test")
}
}
Expected results:
I want to click on the "1" button in the custom UITableViewCell and it show the dropdown menu UIView and be able to click on the buttons contained within the dropdown menu.
I think the problem is that the drop down is added to the super view of the cell (aka the tableView) and the delegate of the dropdown is assigned to the cell itself hence it won't respond to the tap gesture recognizer since the table view is not the delegate of it and the tap should occur on the cell, try adding it to the subView of the cell instead of the superView maybe that'll work. Good luck 👍
I have made an application with a button that is custom made programmatically. I wanted to add an #IBAction to it programmatically and I used the addTarget function to do so. The code does run but when the button is pressed, the function doesn't get called.
import UIKit
class MenuViewController: UIViewController {
#IBOutlet var LanguageLbl: UILabel!
var LanguageBtn = DropdownBtn()
var languagesArray = ["English" , "Русски" , "Español"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.layoutIfNeeded()
self.view.backgroundColor = UIColor.black
setupButtons()
}
func setupButtons() {
LanguageBtn = DropdownBtn.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
LanguageBtn.translatesAutoresizingMaskIntoConstraints = false
LanguageBtn.setTitle("English", for: .normal)
print("Adding target")
LanguageBtn.addTarget(self, action: #selector(languageBtnPressed(_:)), for: .touchUpInside)
print("Succesfully added target")
self.view.addSubview(LanguageBtn)
self.view.layoutIfNeeded()
LanguageBtn.leftAnchor.constraint(equalTo: LanguageLbl.leftAnchor, constant: 210).isActive = true
LanguageBtn.centerYAnchor.constraint(equalTo: LanguageLbl.centerYAnchor).isActive = true
LanguageBtn.widthAnchor.constraint(equalToConstant: 115).isActive = true
LanguageBtn.heightAnchor.constraint(equalToConstant: 40).isActive = true
LanguageBtn.dropView.dropDownOptions = languagesArray
}
#objc func languageBtnPressed(_ sender: UIButton) {
print("Btn Pressed")
}
In my example I have set the target for LanguageBtn called languageBtnPressed and when I run the code I get:
Adding target
Successfully added target
However, when I click the button it doesn't print:
Btn Pressed
What can be the problem here?
Here is my DropDownBtn class:
import Foundation
import UIKit
class DropdownBtn : UIButton, DropdownProtocol {
func dropDownPressed(string: String) {
self.setTitle(string, for: .normal)
self.dismissDropDown()
}
var dropView = DropdownView()
var height = NSLayoutConstraint()
var isOpen = false
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
dropView = DropdownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
dropView.delegate = self
dropView.translatesAutoresizingMaskIntoConstraints = false
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropView)
self.superview?.bringSubview(toFront: dropView)
dropView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = dropView.heightAnchor.constraint(equalToConstant: 0)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([self.height])
if self.dropView.tableView.contentSize.height > 150 {
self.height.constant = 150
} else {
self.height.constant = self.dropView.tableView.contentSize.height
}
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.layoutIfNeeded()
self.dropView.center.y += self.dropView.frame.height / 2
}, completion: nil)
} else {
dismissDropDown()
}
}
func dismissDropDown() {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
and here is my DropDownProtocol
import Foundation
import UIKit
protocol DropdownProtocol {
func dropDownPressed(string: String)
}
and here is DropDownView class
import Foundation
import UIKit
class DropdownView : UIView, UITableViewDelegate, UITableViewDataSource {
var dropDownOptions = [String]()
var tableView = UITableView()
var delegate : DropdownProtocol!
override init(frame: CGRect) {
super.init(frame: frame)
tableView.backgroundColor = UIColor.darkGray
self.backgroundColor = UIColor.darkGray
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(tableView)
tableView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dropDownOptions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = dropDownOptions[indexPath.row]
cell.backgroundColor = UIColor.darkGray
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate.dropDownPressed(string: dropDownOptions[indexPath.row])
self.tableView.deselectRow(at: indexPath, animated: true)
}
}
I am making a pop up menu in Swift on Xcode 8.2.1, and I can't figure out how to make it segue correctly (It is being created programmatically right now so that it will have a animation when you open it).
import UIKit
var clickj = false
var st = String()
class ViewController: UIViewController {
var button = dropDownBtn()
override func viewDidLoad() {
super.viewDidLoad()
if clickj == true {
performSegue(withIdentifier: st, sender: nil)
}
// Do any additional setup after loading the view, typically from a nib.
//Configure the button
button = dropDownBtn.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
button.setTitle("Colors", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
//Add Button to the View Controller
self.view.addSubview(button)
//button Constraints
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.heightAnchor.constraint(equalToConstant: 40).isActive = true
//Set the drop down menu's options
button.dropView.dropDownOptions = ["Blue", "Choices"]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
protocol dropDownProtocol {
func dropDownPressed(string : String)
}
class dropDownBtn: UIButton, dropDownProtocol {
func dropDownPressed(string: String) {
print(string)
st = string
clickj = true
self.dismissDropDown()
}
var dropView = dropDownView()
var height = NSLayoutConstraint()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
dropView = dropDownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
dropView.delegate = self
dropView.translatesAutoresizingMaskIntoConstraints = false
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropView)
self.superview?.bringSubview(toFront: dropView)
dropView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = dropView.heightAnchor.constraint(equalToConstant: 0)
}
var isOpen = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([self.height])
if self.dropView.tableView.contentSize.height > 150 {
self.height.constant = 150
} else {
self.height.constant = self.dropView.tableView.contentSize.height
}
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.layoutIfNeeded()
self.dropView.center.y += self.dropView.frame.height / 2
}, completion: nil)
} else {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
}
func dismissDropDown() {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class dropDownView: UIView, UITableViewDelegate, UITableViewDataSource {
var dropDownOptions = [String]()
var tableView = UITableView()
var delegate : dropDownProtocol!
override init(frame: CGRect) {
super.init(frame: frame)
tableView.backgroundColor = UIColor.darkGray
self.backgroundColor = UIColor.darkGray
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(tableView)
tableView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dropDownOptions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = dropDownOptions[indexPath.row]
cell.backgroundColor = UIColor.darkGray
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate.dropDownPressed(string: dropDownOptions[indexPath.row])
self.tableView.deselectRow(at: indexPath, animated: true)
}
}
The problem is I have every thing set up to segue properly but I am still working on where I should place the if statement that will segue when the condition is true (mainly I am looking for some function I could place it in that would be called in the main ViewController when the button is clicked).
You are close... about all you need is to implement another delegate protocol.
Currently, your dropDownView is using dropDownProtocol to tell the button that a row was selected. What you also want is a protocol so the button can tell the view controller that it has dismissed itself, and to pass the selected string from the table.
Here is the code you presented, with very few changes. See the comments in the code. With your start, things should be pretty clear - feel free to ask for clarification if needed:
import UIKit
class ViewController: UIViewController, dropDownCallBackProtocol {
var button = dropDownBtn()
override func viewDidLoad() {
super.viewDidLoad()
//Configure the button
button = dropDownBtn.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
button.setTitle("Colors", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
//Add Button to the View Controller
self.view.addSubview(button)
//button Constraints
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.heightAnchor.constraint(equalToConstant: 40).isActive = true
//Set the drop down menu's options
button.dropView.dropDownOptions = ["Blue", "Choices"]
// set the button's "vcCallBackDelegate"
button.vcCallBackDelegate = self
}
func dropDownCompleted(string: String) {
print("Inside View Controller:", string)
// this will be called when the un-show-drop-down animation is finished
// so you can perform your segue here
// performSegue(withIdentifier: string, sender: nil)
}
}
// protocol for the button to "call back" to the view controller (its parent)
protocol dropDownCallBackProtocol {
func dropDownCompleted(string : String)
}
// protocol for the table to "call back" to the button (its parent)
protocol dropDownProtocol {
func dropDownPressed(string : String)
}
class dropDownBtn: UIButton, dropDownProtocol {
var vcCallBackDelegate: dropDownCallBackProtocol?
func dropDownPressed(string: String) {
print("Inside button class:", string)
self.dismissDropDown(string)
}
var dropView = dropDownView()
var height = NSLayoutConstraint()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
dropView = dropDownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
dropView.delegate = self
dropView.translatesAutoresizingMaskIntoConstraints = false
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropView)
self.superview?.bringSubview(toFront: dropView)
dropView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = dropView.heightAnchor.constraint(equalToConstant: 0)
}
var isOpen = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([self.height])
if self.dropView.tableView.contentSize.height > 150 {
self.height.constant = 150
} else {
self.height.constant = self.dropView.tableView.contentSize.height
}
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.layoutIfNeeded()
self.dropView.center.y += self.dropView.frame.height / 2
}, completion: nil)
} else {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
}
func dismissDropDown(_ selectedString: String) {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: {
b in
// tell the delegate the animation is complete, and pass the selected string
self.vcCallBackDelegate?.dropDownCompleted(string: selectedString)
})
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class dropDownView: UIView, UITableViewDelegate, UITableViewDataSource {
var dropDownOptions = [String]()
var tableView = UITableView()
var delegate : dropDownProtocol!
override init(frame: CGRect) {
super.init(frame: frame)
tableView.backgroundColor = UIColor.darkGray
self.backgroundColor = UIColor.darkGray
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(tableView)
tableView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dropDownOptions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = dropDownOptions[indexPath.row]
cell.backgroundColor = UIColor.darkGray
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate.dropDownPressed(string: dropDownOptions[indexPath.row])
self.tableView.deselectRow(at: indexPath, animated: true)
}
}