I'm trying to add a floating action button (not a floating menu button) which will navigate me to the next view controller with a single click. I'm not getting the floating button right. I have tried the below code and it is not showing the appropriate button on the table view as it is getting scrolled along with the table. Is there any way to stick the button at the same place without getting scrolled along with the table?
func floatingButton(){
let btn = UIButton(type: .custom)
btn.frame = CGRect(x: 285, y: 485, width: 100, height: 100)
btn.setTitle("All Defects", for: .normal)
btn.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
btn.clipsToBounds = true
btn.layer.cornerRadius = 50
btn.layer.borderColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
btn.layer.borderWidth = 3.0
btn.addTarget(self,action: #selector(DestinationVC.buttonTapped), for: UIControlEvent.touchUpInside)
view.addSubview(btn)
}
All subviews added to UITableView will automatically scroll with it.
What you can do is add the button to the application Window, just remember to remove it when the ViewController disappears.
var btn = UIButton(type: .custom)
func floatingButton(){
btn.frame = CGRect(x: 285, y: 485, width: 100, height: 100)
btn.setTitle("All Defects", for: .normal)
btn.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
btn.clipsToBounds = true
btn.layer.cornerRadius = 50
btn.layer.borderColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
btn.layer.borderWidth = 3.0
btn.addTarget(self,action: #selector(DestinationVC.buttonTapped), for: UIControlEvent.touchUpInside)
if let window = UIApplication.shared.keyWindow {
window.addSubview(btn)
}
}
and in viewWillDisappear:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
btn.removeFromSuperview()
}
For iOS 13 and above you can check for the key window using this extension:
extension UIWindow {
static var key: UIWindow? {
if #available(iOS 13, *) {
return UIApplication.shared.windows.first { $0.isKeyWindow }
} else {
return UIApplication.shared.keyWindow
}
}
}
Here's a working example on XCode 10 with Swift 4.2.
Note that the FAB button will disappear when the view disappears, and reappears when the controller is loaded again.
import UIKit
class ViewController: UITableViewController {
lazy var faButton: UIButton = {
let button = UIButton(frame: .zero)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .blue
button.addTarget(self, action: #selector(fabTapped(_:)), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let view = UIApplication.shared.keyWindow {
view.addSubview(faButton)
setupButton()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let view = UIApplication.shared.keyWindow, faButton.isDescendant(of: view) {
faButton.removeFromSuperview()
}
}
func setupTableView() {
tableView.backgroundColor = .darkGray
}
func setupButton() {
NSLayoutConstraint.activate([
faButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -36),
faButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -36),
faButton.heightAnchor.constraint(equalToConstant: 80),
faButton.widthAnchor.constraint(equalToConstant: 80)
])
faButton.layer.cornerRadius = 40
faButton.layer.masksToBounds = true
faButton.layer.borderColor = UIColor.lightGray.cgColor
faButton.layer.borderWidth = 4
}
#objc func fabTapped(_ button: UIButton) {
print("button tapped")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return UITableViewCell()
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 64
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 32
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return UIView()
}
}
If you currently using tableViewController then no , you must subclass UIViewController add UItableView and your floating button to it
Or you may override scollviewDidScroll and change button y according to tableview current offset
drag scrollview as IBOutlet and set it's delegate to the viewController
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let off = scrollView.contentOffset.y
btn.frame = CGRect(x: 285, y: off + 485, width: btn.frame.size.width, height: btn.frame.size.height)
}
code snapshot
in action
see in action
There is a way that is worked for me. In my table view controller, defined a button first, then override the function scrollViewDidScroll(), like this:
import UIKit
class MyTableViewController: UITableViewController {
private let button = UIButton(type: UIButton.ButtonType.custom) as UIButton
override func viewDidLoad() {
let bottomImage = UIImage(named: "yourImage.png")
let yPst = self.view.frame.size.height - 55 - 20
button.frame = CGRect(x: 12, y: yPst, width: 55, height: 55)
button.setImage(bottomImage, for: .normal)
button.autoresizingMask = [.flexibleTopMargin, .flexibleRightMargin]
button.addTarget(self, action: #selector(buttonClicked(_:)), for:.touchUpInside)
button.layer.shadowRadius = 3
button.layer.shadowColor = UIColor.lightGray.cgColor
button.layer.shadowOpacity = 0.9
button.layer.shadowOffset = CGSize.zero
button.layer.zPosition = 1
view.addSubview(button)
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let off = self.tableView.contentOffset.y
let yPst = self.view.frame.size.height
button.frame = CGRect(x: 12, y:off + yPst, width: button.frame.size.width, height: button.frame.size.height)
}
#objc private func buttonClicked(_ notification: NSNotification) {
// do something when you tapped the button
}
}
Related
I have a design in which the sign-out button is placed at the bottom and should not scroll, side menu has a tableview controller in which we can add rows but my requirement is to add the sign-out button at the bottom. I have tried by adding the sign-out button in the footer view to side menu tableview controller, but showing just bellow the rows which I don't want.
import UIKit
import SideMenu
class ProfileViewController: UIViewController {
var menu:SideMenuNavigationController?
override func viewDidLoad() {
super.viewDidLoad()
configureSideMenu()
}
func configureSideMenu() {
menu = SideMenuNavigationController(rootViewController: MenuListController())
menu?.navigationBar.setBackgroundImage(UIImage(named: "top_navbrBG"), for: .default)
let firstFrame = CGRect(x: 20, y: 0, width: menu?.navigationBar.frame.width ?? 0/2, height: menu?.navigationBar.frame.height ?? 0)
let firstLabel = UILabel(frame: firstFrame)
firstLabel.text = "Settings"
menu?.navigationBar.addSubview(firstLabel)
SideMenuManager.default.addPanGestureToPresent(toView: self.view)
let screenSize = UIScreen.main.bounds
let screenHeight = screenSize.height + 40
let leftBorderView = UIView(frame: CGRect(x: 1, y: -40, width: 1, height: screenHeight))
leftBorderView.backgroundColor = UIColor.init(hexString: "#cfcfcf")
menu?.navigationBar.addSubview(leftBorderView)
}
#IBAction func menuButtonAction(_ sender: UIButton) {
if let menu = menu {
present(menu, animated: true)
}
}
}
// functions
extension ProfileViewController {
#objc func signOutButtonTapped(_ sender: AnyObject?) {
print("sigin out")
}
func getSignOutButton()->UIButton {
let button = UIButton()
button.setTitle("Sign out", for: .normal)
let color = UIColor.init(hexString: "#1A73E9")
button.setTitleColor(color, for: .normal)
button.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)
return button
}
func setConstraintsForSignOutButton(button: UIButton) {
let screenSize = UIScreen.main.bounds
let screenHeight = screenSize.height
guard let menuTopAnchor = menu?.navigationBar.topAnchor else { return }
guard let menuLeadingAnchor = menu?.navigationBar.leadingAnchor else { return }
guard let menuTrailingAnchor = menu?.navigationBar.trailingAnchor else { return }
guard let menuWidth = menu?.menuWidth else { return }
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: menuLeadingAnchor, constant: 0),
button.trailingAnchor.constraint(equalTo: menuTrailingAnchor, constant: 0),
button.widthAnchor.constraint(equalToConstant: menuWidth),
button.heightAnchor.constraint(equalToConstant: 40),
button.bottomAnchor.constraint(equalTo: menuTopAnchor, constant: 300)
])
}
}
// Menu Items
class MenuListController: UITableViewController {
var menuItems = [[String: String]]()
override func viewDidLoad() {
super.viewDidLoad()
menuItems.append(["name": "Privacy", "img": "privacy", "key" : "privacy"])
menuItems.append(["name": "Report issue", "img": "report_issue", "key": "report_issue"])
tableView.dataSource = self
tableView.delegate = self
tableView.separatorStyle = .none
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.tableFooterView = getSignOutButton()
}
#objc func signOutButtonTapped(_ sender: AnyObject?) {
print("sigin out")
}
func getSignOutButton()->UIButton {
let button = UIButton()
button.height = 20
button.width = 100
button.setTitle("Sign out", for: .normal)
let color = UIColor.init(hexString: "#1A73E9")
button.setTitleColor(color, for: .normal)
button.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)
return button
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
menuItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if menuItems.indices.contains(indexPath.row) {
let dict = menuItems[indexPath.row]
cell.textLabel?.text = dict["name"]
if let img = dict["img"] {
cell.imageView?.image = UIImage(named: img)
}
}
return cell
}
}
desired design
Replace this "tableView.tableFooterView = getSignOutButton()" with
self.getSignOutButton() in viewDidLoad.
Next replace your function "getSignOutButton()->UIButton {}" with below code.
func getSignOutButton() {
let button = UIButton()
button.setTitle("Sign out", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
button.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
button.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor,constant: -20),
button.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
])
}
I have a uitableview
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let width = tableView.frame.width - CGFloat((start*2))
let headerView = UIView()
headerView.frame.origin.x = 0
headerView.frame.origin.y = -45
headerView.frame.size.width = tableView.frame.width
let desc = UILabel()
desc.frame.origin.x = label.frame.origin.x
desc.frame.origin.y = line.frame.maxY + 10
desc.frame.size.width = width
desc.numberOfLines = 0
desc.font = UIFont(name: "Lato-Regular", size: 17);
desc.textColor = UIColor(red: 0.53, green: 0.53, blue: 0.53, alpha: 1.00)
desc.text = self.data.desc
desc.backgroundColor = UIColor.clear
desc.textAlignment = .left
desc.adjustsFontForContentSizeCategory = true
if (loadMoreDesc) {
desc.sizeToFit()
} else {
desc.frame.size.height = 110
}
headerView.addSubview(desc)
let moreButtonDesc = UIButton(frame: CGRect(x: desc.frame.minX, y: desc.frame.maxY, width: 80, height: 15))
moreButtonDesc.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center
moreButtonDesc.titleLabel?.textAlignment = .left
if (loadMoreDesc) {
moreButtonDesc.setTitle("Less", for: .normal)
} else {
moreButtonDesc.setTitle("More", for: .normal)
}
moreButtonDesc.titleLabel?.font = UIFont(name: "Lato-Regular", size: 15);
moreButtonDesc.setTitleColor(UIColor(red: 0.00, green: 0.48, blue: 1.00, alpha: 1.00), for: .normal)
moreButtonDesc.addTarget(self, action:#selector(self.expandDesc(sender:)), for: .touchUpInside)
headerView.addSubview(moreButtonDesc)
When people click on "More" button the description should expand and the height of the table view header should increase to fit the description (desc). How can I accomplish this? Here's my expandDesc function
#objc func expandDesc(sender: UIButton) {
loadMoreDesc = !loadMoreDesc
tableView.reloadData()
}
Please note that the description is of dynamic height. I won’t know the height to set for the header unless I know the height of the description.
please can you try this
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if loadMoreDesc {
return 100
} else {
return 50.0
}
}
I have created the UITableView with the custom UITableViewCell. But the problem which I am getting is the width of the cells is not the frame width though I have assigned in the CGReact. Please have a look over my code :
CustomTableViewCell Class:
import UIKit
class CustomTableViewCell: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width, height: 76))
view.backgroundColor = .red
view.layer.applySketchShadow()
return view
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
lazy var iconTime : UIImageView = {
var object = UIImageView(frame: CGRect(x: 10, y: 54, width: 12, height: 12))
object.image = #imageLiteral(resourceName: "clock")
return object
}()
lazy var notification : UILabel = {
var object = UILabel(frame: CGRect(x: 10, y: 7, width: backView.frame.width, height: 40))
object.adjustsFontSizeToFitWidth = true
object.minimumScaleFactor = 0.5
object.font = object.font.withSize(28.0)
object.numberOfLines = 3
return object
}()
lazy var notificationTime : UILabel = {
var object = UILabel(frame: CGRect(x: 30, y: 40, width: backView.frame.width, height: 40))
object.adjustsFontSizeToFitWidth = true
object.minimumScaleFactor = 0.5
object.font = object.font.withSize(12.0)
return object
}()
override func layoutSubviews() {
contentView.backgroundColor = UIColor.clear
backgroundColor = UIColor.clear
backView.layer.cornerRadius = 5
backView.clipsToBounds = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
addSubview(backView)
[notification, notificationTime, iconTime].forEach(backView.addSubview(_:))
}
}
And my view controller as follows :
import UIKit
class UserModal {
var tableView = UITableView()
var notification: String?
var notificationTime : String?
init(notification: String, notificationTime: String) {
self.notification = notification
self.notificationTime = notificationTime
}
}
class newNotificationController : UIViewController {
var tableView = UITableView()
var userMod = [UserModal]()
override func viewDidLoad() {
super.viewDidLoad()
setTableView()
userMod.append(UserModal(notification: "Data ", notificationTime: "Time"))
userMod.append(UserModal(notification: "This is some Notification which needs to be populated in the Grid view for testing but lets see what is happening here!! ", notificationTime: "12-12-1212 12:12:12"))
userMod.append(UserModal(notification: "Data ", notificationTime: "Time"))
}
func setTableView() {
tableView.frame = self.view.frame
tableView.backgroundColor = UIColor.clear
tableView.delegate = self
tableView.dataSource = self
tableView.separatorColor = UIColor.clear
self.view.addSubview(tableView)
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: animated)
}
}
extension newNotificationController: UITableViewDelegate , UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userMod.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CustomTableViewCell else { fatalError("Unable to populate Notification History")}
cell.notification.text = userMod[indexPath.row].notification
cell.notificationTime.text = userMod[indexPath.row].notificationTime
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 85
}
}
Please have a look over the result:
I am not getting it why the width of my cells is the width of the frame. Any help will be highly appreciated. Thanks!!
The problem is in this code is frame width, somehow the width of the self is not the width of a device, so because of this, you are facing this issue.
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width, height: 76))
view.backgroundColor = .red
view.layer.applySketchShadow()
return view
}()
To resolve this issue you can set frame like this
let view = UIView(frame: CGRect(x: 10, y: 6, width: UIScreen.main.bounds.size.width - 10, height: 76))
You set the width your view
UIView(frame: CGRect(x: 5, y: 6, width: self.frame.width - 10,
height: 76))
tableView.frame = CGRect(x: 0, y: 0, width:
self.view.frame.size.width, height: self.view.frame.size.height)
you need to give constraint to the tableview. Top, Leading, Trailing, Bottom.
put this tableView.translatesAutoresizingMaskIntoConstraints = false line in your function
func setTableView() {
tableView.frame = self.view.frame
tableView.backgroundColor = UIColor.clear
tableView.delegate = self
tableView.dataSource = self
tableView.separatorColor = UIColor.clear
self.view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false //add this line
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
}
and change width: UIScreen.main.bounds.size.width - 10 it.
thanks..
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 added custom tableview header with two buttons, but buttons are disabled , unable to make control events. i want to get layout like this. i'm new to development. any suggestions or solution
i tried to add view with buttons inside view in ViewforHeaderSection function
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let inviteSectionHeaderview = UIView.init(frame: CGRect(x:0, y:0, width: self.view.bounds.width, height: self.view.bounds.height))
let selectAllBtn = UIButton.init(frame:CGRect(x:16, y: inviteSectionHeaderview.bounds.height/2, width:130, height:20))
let sendButton = UIButton.init(frame:CGRect(x:inviteSectionHeaderview.bounds.width - 30, y: inviteSectionHeaderview.bounds.height/2, width:60, height:20))
selectAllBtn.setTitle("select all/Cancel", for: .normal)
selectAllBtn.backgroundColor = .black
sendButton.backgroundColor = .black
sendButton.setTitle("SEND", for: .normal)
self.contactsTable.addSubview(inviteSectionHeaderview)
inviteSectionHeaderview.addSubview(selectAllBtn)
inviteSectionHeaderview.addSubview(sendButton)
return inviteSectionHeaderview
}
You have two options:
Create your UIView in storyboard
Create programatically
Option 2
Create your UIView.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 30.0))
// Button1
let button1 = UIButton(frame: CGRect(x: 15.0, y: 0, width: 100, height: 28.0)
button1.setTitle("Button 1", for: .normal)
button1.addTarget(self, action: #selector(selectorButton1), for: .touchUpInside)
// Button2
let button2 = UIButton(frame: CGRect(x: tableView.frame.width-150, y: 0, width: 150, height: 30.0))
button2.setTitle("Button2", for: .normal)
button2.addTarget(self, action: #selector(selectorButton2), for: .touchUpInside)
button2.semanticContentAttribute = UIApplication.shared
.userInterfaceLayoutDirection == .rightToLeft ? .forceLeftToRight : .forceRightToLeft
headerView.addSubview(button1)
headerView.addSubview(button2)
return headerView
}
#objc func selectorButton1(_ sender : Any) {
}
#objc func selectorButton2(_ sender : Any) {
}
In this case, you must set correctely y and x positions when create UIView(frame: CGRect()) and UIButton(frame: CGRect())
EDIT
From your code, you just need add the targets:
selectAllBtn.addTarget(self, action: #selector(selectorAllBtn), for: .touchUpInside)
sendButton.addTarget(self, action: #selector(selectorSendButton), for: .touchUpInside)
#objc func selectorAllBtn(_ sender : Any) {
}
#objc func selectorSendButton(_ sender : Any) {
}
You can try this code.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
var headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "headerView")
if headerView == nil {
headerView = UITableViewHeaderFooterView(reuseIdentifier: "headerView")
let button1 = UIButton(frame: CGRect(x: 8, y: 8, width: 80, height: 40))
button1.setTitle("Select", for: .normal)
button1.addTarget(self, action: #selector(self.buttonAction), for: .touchUpInside)
headerView?.addSubview(button1)
}
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50 // set a height for header view
}
#objc func buttonAction(_ sender: UIButton) {
// write your code...
}