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)
}
}
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)
}
}
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)
}
}
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'm a bit new to Xcode and been trying to do things programatically. I have View Controller A, B, C, and D. I have a back button on C, and D. When going from D to C using self.dismiss it works fine, however when I go from C to B I am getting a crash that looks like it's a constraint issue and I have no idea why.
Again, the crash occurs when going from C to B. The error says no common ancestor for DropDownButton, but there is no DropDownButton on ViewController B, it exists on C the one I am trying to dismiss.
I would like to know more about how the view controllers dismissing and Auto Layout works, could someone point me in the right direction please?
"oneonone.DropDownButton:0x7fcfe9d30660'🇺🇸+1 ⌄'.bottom"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal. userInfo: (null)
2018-11-09 19:56:22.828322-0600 oneonone[62728:4835265] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutYAxisAnchor
UPDATE TO QUESTIONS:
Here is View Controller C, included is the var, adding it to subview, and how I dismiss this view controller
lazy var countryCodes: DropDownButton = {
let button = DropDownButton(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
let us = flag(country: "US")
let br = flag(country: "BR")
let lightGray = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
button.backgroundColor = lightGray
button.setTitle(us + "+1 \u{2304}", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 20)
button.setTitleColor(UIColor.darkGray, for: .normal)
button.uiView.dropDownOptions = [us + "+1", br + "+55", "+33", "+17", "+19"]
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
[countryCodes].forEach{ view.addSubview($0) }
setupLayout()
}
func setupLayout(){
countryCodes.translatesAutoresizingMaskIntoConstraints = false
countryCodes.topAnchor.constraint(equalTo: instructionLabel.bottomAnchor, constant: 30).isActive = true
countryCodes.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -77.5).isActive = true
countryCodes.widthAnchor.constraint(equalToConstant: 85).isActive = true // guarantees this width for stack
countryCodes.heightAnchor.constraint(equalToConstant: 40).isActive = true
}
#objc func buttonPressed(){
self.dismiss(animated: true, completion: nil)
}
Here is the code in view controller B that (creates or presents?) View Controller C
#objc func phoneAuthButtonPressed(){
let vc = phoneAuthViewController()
self.present(vc, animated: true, completion: nil)
}
UPDATE 2: ADDING THE CUSTOM CLASS
Here is the button code that I used as a custom class following a tutorial, I believe the problem lies in here
protocol dropDownProtocol {
func dropDownPressed(string: String)
}
class DropDownButton: UIButton, dropDownProtocol {
var uiView = DropDownView()
var height = NSLayoutConstraint()
var isOpen = false
func dropDownPressed(string: String) {
self.setTitle(string + " \u{2304}", for: .normal)
self.titleLabel?.font = UIFont.systemFont(ofSize: 18)
self.dismissDropDown()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.gray
uiView = DropDownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
uiView.delegate = self
uiView.layer.zPosition = 1 // show in front of other labels
uiView.translatesAutoresizingMaskIntoConstraints = false
}
override func didMoveToSuperview() {
self.superview?.addSubview(uiView)
self.superview?.bringSubviewToFront(uiView)
uiView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
uiView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
uiView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = uiView.heightAnchor.constraint(equalToConstant: 0)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // animates drop down list
NSLayoutConstraint.deactivate([self.height])
if self.uiView.tableView.contentSize.height > 150 {
self.height.constant = 150
} else {
self.height.constant = self.uiView.tableView.contentSize.height
}
if isOpen == false {
isOpen = true
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut, animations: {
self.uiView.layoutIfNeeded()
self.uiView.center.y += self.uiView.frame.height / 2
}, completion: nil)
} else {
dismissDropDown()
}
}
func dismissDropDown(){
isOpen = false
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut, animations: {
self.uiView.center.y -= self.uiView.frame.height / 2
self.uiView.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!
let lightGray = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
override init(frame: CGRect) {
super.init(frame: frame)
tableView.backgroundColor = lightGray
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(tableView) // can not come after constraints
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.trailingAnchor).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.textLabel?.font = UIFont.systemFont(ofSize: 14)
cell.textLabel?.textColor = UIColor.darkGray
cell.backgroundColor = lightGray
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate.dropDownPressed(string: dropDownOptions[indexPath.row])
self.tableView.deselectRow(at: indexPath, animated: true)
}
}
Replace your didMoveToSuperview function with this and it will work. This function also gets called when the view its removed from the superview and the superview will be nil and that's causing the crash.
override func didMoveToSuperview() {
if let superview = self.superview {
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)
}
}
As I can see, the error says that one of countryCodes buttons is located in the different view than instructionLabel. They should have the same parent if you want them to be constrained by each other.
Hi I think the problem occurs in the didMoveToSuperView() function, because it is called also when the view is removed from it's superview. so when you try to setup the anchor to something that does no more exist it crashes.
try something like this :
if let superview = self.superview {
superview.addSubview(uiView)
superview.bringSubviewToFront(uiView)
uiView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
uiView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
uiView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = uiView.heightAnchor.constraint(equalToConstant: 0)
}
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)
}
}