Display user's post on a new ViewController - ios

I'm working on an app that let's users post, like and comment, so as part of my comment implementation, I'm trying to display the user's post, so you are able to comment on it. I am using a UITableView that is the main timeline and it displays all of the posts users have made, so what I'm trying to do is that when a user clicks on a cell, it displays the post of that cell so you are able to comment on it, but I'm having trouble displaying it.
This is the code that I have for that implementation so far, so what am I missing?
Is there anything else that I can do to be able to display the selected post in a viewController after the user clicks on the cell?
import UIKit
import Parse
class DetailViewContoller: UIViewController, UITableViewDelegate, UITextViewDelegate {
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var postLabel: UILabel!
#IBOutlet weak var commentTableView: UITableView!
var post: PFObject?
var commentView: UITextView?
var footerView: UIView?
var contentHeight: CGFloat = 0
var comments: [String]?
let FOOTERHEIGHT : CGFloat = 50;
override func viewDidLoad() {
super.viewDidLoad()
commentTableView.delegate = self
/* Setup the keyboard notifications */
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyBoardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyBoardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
if(post?.objectForKey("comments") != nil) {
comments = post?.objectForKey("comments") as? [String]
}
println(post)
println(post?.objectForKey("content"))
self.postLabel.text = post?.objectForKey("content") as? String
}
func keyBoardWillShow(notification: NSNotification) {
var info:NSDictionary = notification.userInfo!
var keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
var keyboardHeight:CGFloat = keyboardSize.height - 40
var animationDuration:CGFloat = info[UIKeyboardAnimationDurationUserInfoKey] as! CGFloat
var contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
self.commentTableView.contentInset = contentInsets
self.commentTableView.scrollIndicatorInsets = contentInsets
}
func keyBoardWillHide(notification: NSNotification) {
self.commentTableView.contentInset = UIEdgeInsetsZero
self.commentTableView.scrollIndicatorInsets = UIEdgeInsetsZero
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let count = comments?.count {
return count
}
return 0
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = commentTableView.dequeueReusableCellWithIdentifier("commentCell", forIndexPath: indexPath) as! CommentTableViewCell
cell.commentLabel?.text = comments![indexPath.row]
return cell
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
if self.footerView != nil {
return self.footerView!.bounds.height
}
return FOOTERHEIGHT
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
footerView = UIView(frame: CGRect(x: 0, y: 0, width: commentTableView.bounds.width, height: FOOTERHEIGHT))
footerView?.backgroundColor = UIColor(red: 243.0/255, green: 243.0/255, blue: 243.0/255, alpha: 1)
commentView = UITextView(frame: CGRect(x: 10, y: 5, width: commentTableView.bounds.width - 80 , height: 40))
commentView?.backgroundColor = UIColor.whiteColor()
commentView?.textContainerInset = UIEdgeInsetsMake(5, 5, 5, 5)
commentView?.layer.cornerRadius = 2
commentView?.scrollsToTop = false
footerView?.addSubview(commentView!)
let button = UIButton(frame: CGRect(x: commentTableView.bounds.width - 65, y: 10, width: 60 , height: 30))
button.setTitle("Reply", forState: UIControlState.Normal)
button.backgroundColor = UIColor(red: 0/0.0, green: 179/255.0, blue: 204/255.0, alpha: 100.0/100.0)
button.layer.cornerRadius = 5
button.addTarget(self, action: "reply", forControlEvents: UIControlEvents.TouchUpInside)
footerView?.addSubview(button)
commentView?.delegate = self
return footerView
}
//Hide keyboard after touching background
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
self.view.endEditing(true)
}
//remaining characters
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool{
if (text == "\n") {
textView.resignFirstResponder()
}
return true
}
func reply() {
post?.addObject(commentView!.text, forKey: "comments")
post?.saveInBackground()
if let tmpText = commentView?.text {
comments?.append(tmpText)
}
commentView?.text = ""
println(comments?.count)
self.commentView?.resignFirstResponder()
self.commentTableView.reloadData()
}
}

I don't see anything in your code that actually fires when you click on a table cell. The way I would modify your code is to:
Implement override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {}
Use indexpath to determine the object you want to display detail for
Create a new viewcontroller for the detail, pass the object to it and then push it
A more elegant (but complex, in some ways) way of doing things is to implement PFQueryTableViewController and then use the function objectAtIndexPath() to retrieve the relevant object and display detail

Related

tableview last section is scrambled when setting row height to 0 swift

I added an arrow to fold the section when clicked, when I fold the last section (e.g. setting the row height to 0 in cellForRow, all the rows in the last section get mixed up, as can be seen in the image below:
Can anyone suggest any reason why this should happen if I'm setting the height of the row to 0?
Here's the relevant code:
viewForHeaderInSection:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let profileCompletion = clientFields?.profileCompletion ?? 0
headerView.profileComplete.text = "Profile".localized() + " \(profileCompletion)% " + "Complete".localized()
headerView.profileProgress.progress = Float(profileCompletion) * 0.01
headerView.profileProgress.progressTintColor = Constants.Client.getProfileCompletenessColor(profileCompletion)
headerView.delegate = self
headerView.section = section
headerView.isOpen = self.sectionsStatuses[section].shouldDisplaySection
return headerView
}
else {
let height = (section == 0) ? FIRST_HEADER_HEIGHT : HEADER_HEIGHT
let nib = UINib(nibName: "ClientDetailsSectionHeaderCell", bundle: nil)
guard let returnedView = nib.instantiate(withOwner: self, options: nil)[0] as? ClientDetailsSectionHeaderCell else {
return UIView()
}
returnedView.delegate = self
returnedView.section = section
returnedView.isOpen = self.sectionsStatuses[section].shouldDisplaySection
returnedView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: height)
returnedView.heightConstraint.constant = height
returnedView.mainView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
returnedView.mainView.backgroundColor = Constants.ParentForm.headerBgColor
// Draw separators
if DRAW_SECTION_SEPARATORS || DRAW_SECTION_TOP_SEPARATOR {
returnedView.createTopSeparator(color: Constants.ParentForm.separatorColor)
}
if DRAW_SECTION_SEPARATORS || DRAW_SECTION_BOTTOM_SEPARATOR {
returnedView.createBottomSeparator(color: Constants.ParentForm.separatorColor)
}
if isSectionTitleHidden == false {
let xOffset = HEADER_X_OFFSET
let yOffset = section == 0 ? FIRST_HEADER_Y_OFFSET : HEADER_Y_OFFSET
returnedView.title.frame = CGRect(x: xOffset, y: yOffset, width: view.frame.size.width - xOffset, height: height - yOffset)
returnedView.title.text = self.sectionsArray[section].uppercased().localized()
returnedView.title.font = Constants.ParentForm.headerFont
returnedView.title.textColor = Constants.ParentForm.headerTextColor
returnedView.title.sizeToFit()
}
return returnedView
}
}
heightForRow
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if sectionsStatuses[indexPath.section].shouldDisplaySection {
return super.tableView(tableView, heightForRowAt: indexPath)
}
return CGFloat(0)
}
cellForRow
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if self.sectionsArray[indexPath.section] != "ADDRESSES" {
if let clientFields = self.clientFields {
self.dataMap = Utils.convertObjectToMap(item: clientFields)
let birthdayDate = clientFields.birthdayDateString
self.dataMap["birthdayDate"] = birthdayDate
let createdDate = clientFields.createdDateString
self.dataMap["createdDate"] = createdDate
}
}
else {
if let clientAddresses = self.clientAddresses, clientAddresses.count > 0 {
self.dataMap = Utils.convertObjectToMap(item: clientAddresses[Int(floor(Double(indexPath.row) / 8.0))])
}
}
return super.tableView(tableView, cellForRowAt: indexPath)
}
protocol function
func openCloseSection(openSection: Bool, section: Int) {
self.sectionsStatuses[section].shouldDisplaySection = openSection
self.tableView.reloadData()
}
ClientDetailsSectionHeaderCell
import UIKit
protocol SectionViewerProtocol {
func openCloseSection(openSection: Bool, section: Int)
}
class ClientDetailsSectionHeaderCell: UITableViewCell {
#IBOutlet weak var mainView: UIView!
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
#IBOutlet weak var accessoryButton: UIButton!
#IBOutlet weak var title: UILabel!
var section = 0
var delegate: SectionViewerProtocol?
var isOpen: Bool? {
didSet {
if let isOpen = self.isOpen {
accessoryButton.setImage(UIImage(named: isOpen ? "fill588": "fill589"), for: .normal)
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func openCloseSection(_ sender: UIButton) {
if let isOpen = self.isOpen {
self.isOpen = !isOpen
delegate?.openCloseSection(openSection: !isOpen, section: section)
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Try setting clipsToBounds of your cell or any enclosing UIView to true.

How to fix dynamic height for individual cells as UIautodimensions is not working

I am creating a wall page like twitter or Facebook in which user can post on the wall it can include image and just text. So based on the content the cell's heights must change, if the post is of one line the row height must be appropriate.
I tried using UITableViewAutomaticDimensions, but its not working with my code please do help me.
import UIKit
class wallPageViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableOfApps : UITableView?
var appNames: [String]? = []
var apps: [wallPageModel]?
var dynamic_height : CGFloat = 150.0
var addPost : UITextField?
var readMore : UITextView?
func loadTableOfApps(){
//appNames = ["jkbajdba", "abfajfb", "akjbfkjag", "ahbfkajf" ]
let provider = wallPageProvider()
apps = provider.getApps()
let tableHeight = view.frame.size.height
tableOfApps = UITableView(frame: CGRect(x: 10, y: 60, width: 300, height: tableHeight))
tableOfApps!.delegate = self
tableOfApps!.dataSource = self
tableOfApps?.separatorStyle = UITableViewCellSeparatorStyle.none
//self.tableOfApps?.rowHeight = UITableViewAutomaticDimension
//self.tableOfApps?.estimatedRowHeight = UITableViewAutomaticDimension
print("load table of apps")
view.addSubview(tableOfApps!)
}
override func viewDidLoad() {
super.viewDidLoad()
loadTableOfApps()
loadAddPost()
tableOfApps?.estimatedRowHeight = 150.0
tableOfApps?.rowHeight = UITableViewAutomaticDimension
view.backgroundColor = UIColor(red: 232/255, green: 234/255, blue: 246/255, alpha: 1.0)
// Do any additional setup after loading the view.
}
func loadAddPost(){
addPost = UITextField(frame: CGRect(x: 10, y: 20, width: 300, height: 30))
addPost?.placeholder = "Stay safe, post a tip!"
addPost?.isUserInteractionEnabled = true
addPost?.borderStyle = UITextBorderStyle.roundedRect
addPost?.autocapitalizationType = .none
view.addSubview(addPost!)
addPost?.addTarget(self, action: #selector(writeComment), for: UIControlEvents.touchDown)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("app coun\(apps!.count)")
return apps!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell : wallModelTableViewCell?
cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? wallModelTableViewCell
if cell == nil {
cell = wallModelTableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
}
let app = apps![indexPath.row]//reading the array of data hence provider
//association of provider and view
//this is the processs from where view can read the data from provide
cell!.iconImageLabel!.image = app.icon
cell!.titleLabel!.text = app.title
cell?.backgroundColor = UIColor.white
cell!.commentsLabel?.image = app.comments_Label
cell?.commentsLabel?.center = CGPoint(x: tableView.frame.size.width/2, y: dynamic_height-20)
cell?.likeButtonLabel?.image = app.likeButton
cell?.likeButtonLabel?.center = CGPoint(x: (tableView.frame.size.width/2)-130, y: dynamic_height-20)
/*cell?.likeButtonLabel?.leftAnchor.constraint(equalTo: tableView.leftAnchor, constant: 50)*/
cell!.feedTextLabel!.text = app.feedText
cell?.feedTextLabel?.sizeToFit()//so that it can expand the data more than two lines
cell?.feedTextLabel?.backgroundColor = UIColor.clear
//Limiting UIlabel to 125 characters
if (cell?.feedTextLabel?.text.count)! > 125{
/*
let index = cell?.feedTextLabel?.text.index((cell?.feedTextLabel?.text.startIndex)!, offsetBy: 125)
cell?.feedTextLabel?.text = cell?.feedTextLabel?.text.substring(to: index!)
cell?.feedTextLabel?.text = cell?.feedTextLabel?.text.appending("...")*/
//cell?.feedTextLabel?.attributedText = NSAttributedString(string: "...readmore")
}
cell?.layer.cornerRadius = 6.0
cell?.isUserInteractionEnabled = false
cell?.titleLabel?.isUserInteractionEnabled = true
tableOfApps?.backgroundColor = UIColor.clear
tableOfApps?.backgroundView?.isOpaque = false
tableOfApps!.estimatedRowHeight = 150.0
tableOfApps!.rowHeight = UITableViewAutomaticDimension
print("table view cell for row")
/*let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector( self.labelAction(gesture:)))
cell?.feedTextLabel?.addGestureRecognizer(tap)
cell?.feedTextLabel?.isUserInteractionEnabled = true
tap.delegate = self as! UIGestureRecognizerDelegate*/
return cell!
}
/*#objc func labelAction(gesture: UITapGestureRecognizer){
}*/
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
print("height for row")
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 150.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Navigation
#objc func writeComment(){
let addComment = addCommentViewController()
navigationController?.pushViewController(addComment, animated: true)
}
/*
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I want each row must have dynamic height as the content is provided by the user if the content contains 150 characters the height of the cell must be 200+ if content contains less than 125 characters the height must be as adequate to read.

How To Share Same UIView Between Multiple UITableView Cells

Objective
When the user clicks on any of the 3 blue buttons, all buttons change to the same color.
Note: this is an abstraction of a shared progress view problem, it's therefore important that only one UIView is shared (or mimicked) across my three rows
Here is a compilable Swift project:
import UIKit
class ToggleButton: UIButton {
var connectedView: UIView?
func onPress() {
self.isHidden = true
self.connectedView?.isHidden = false
}
}
class ViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
var tableView: UITableView = UITableView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
var myView: UIView? = nil
var toggleBtn: ToggleButton? = nil
override func viewDidLoad() {
self.setupTableView()
}
fileprivate func setupTableView() {
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.backgroundColor = UIColor.white
self.tableView.isOpaque = true
self.view.addSubview(self.tableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "CellIdentifier")
let frame = CGRect(x: 10, y: 10, width: 30, height: 30)
if let view = self.myView, let btn = self.toggleBtn {
cell.addSubview(view)
cell.addSubview(btn)
} else {
let myView = UIView(frame: frame)
myView.backgroundColor = UIColor.green
myView.isHidden = true
cell.addSubview(myView)
let toggleBtn = ToggleButton(frame: frame)
toggleBtn.backgroundColor = UIColor.blue
toggleBtn.addTarget(self, action: #selector(onPress), for: .touchUpInside)
toggleBtn.connectedView = myView
cell.addSubview(toggleBtn)
}
return cell
}
#objc func onPress(_ sender: Any) {
if let button = sender as? ToggleButton {
button.onPress()
}
}
}
Any help appreciated.
The concept of UITableViewCell is made to be very independent each other.
So the only thing you can do it having a bool flag in your ViewController, then you init your 3 cells with this flags.
And finally each time the button is pressed you toggle the flag en reload your tableView.

Blank Menu Button with Swift Slide Menu

I have a weird issue when creating the Slide out menu in swift,
I'm building the menu using AKSwiftSlideMenu as a reference,
This only happens on the ViewControllers that have a UITableViewDataSource, UITableViewDelegate.
If I go to the other view controllers without one, the menu shows up fine.
Below is my code for BaseViewController
class BaseViewController: UIViewController, SlideMenuDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func slideMenuItemSelectedAtIndex(_ index: Int32) {
let topViewController : UIViewController = self.navigationController!.topViewController!
print("View Controller is : \(topViewController) \n", terminator: "")
switch(index){
case 0:
print("Locations\n", terminator: "")
self.openViewControllerBasedOnIdentifier("Locations")
break
case 1:
print("Offers\n", terminator: "")
self.openViewControllerBasedOnIdentifier("Offers")
break
case 2:
print("Feedback\n", terminator: "")
self.openViewControllerBasedOnIdentifier("Feedback")
break
case 3:
print("About\n", terminator: "")
self.openViewControllerBasedOnIdentifier("About")
break
case 4:
for key in UserDefaults.standard.dictionaryRepresentation().keys {
UserDefaults.standard.removeObject(forKey: key)
}
//fb logout
if(FBSDKAccessToken.current() != nil) {
FBSDKAccessToken.setCurrent(nil)
FBSDKProfile.setCurrent(nil)
}
self.openViewControllerBasedOnIdentifier("SocialLogin")
default:
print("default\n", terminator: "")
}
}
func openViewControllerBasedOnIdentifier(_ strIdentifier:String){
let destViewController : UIViewController = self.storyboard!.instantiateViewController(withIdentifier: strIdentifier)
let topViewController : UIViewController = self.navigationController!.topViewController!
if (topViewController.restorationIdentifier! == destViewController.restorationIdentifier!){
print("Same VC")
} else {
self.navigationController!.pushViewController(destViewController, animated: true)
}
}
func addSlideMenuButton(){
let btnShowMenu = UIButton(type: UIButtonType.system)
btnShowMenu.setImage(self.defaultMenuImage(), for: UIControlState())
btnShowMenu.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
btnShowMenu.addTarget(self, action: #selector(BaseViewController.onSlideMenuButtonPressed(_:)), for: UIControlEvents.touchUpInside)
let customBarItem = UIBarButtonItem(customView: btnShowMenu)
self.navigationItem.leftBarButtonItem = customBarItem;
}
func defaultMenuImage() -> UIImage {
var defaultMenuImage = UIImage()
UIGraphicsBeginImageContextWithOptions(CGSize(width: 30, height: 22), false, 0.0)
UIColor.black.setFill()
UIBezierPath(rect: CGRect(x: 0, y: 3, width: 30, height: 1)).fill()
UIBezierPath(rect: CGRect(x: 0, y: 10, width: 30, height: 1)).fill()
UIBezierPath(rect: CGRect(x: 0, y: 17, width: 30, height: 1)).fill()
UIColor.white.setFill()
UIBezierPath(rect: CGRect(x: 0, y: 4, width: 30, height: 1)).fill()
UIBezierPath(rect: CGRect(x: 0, y: 11, width: 30, height: 1)).fill()
UIBezierPath(rect: CGRect(x: 0, y: 18, width: 30, height: 1)).fill()
defaultMenuImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return defaultMenuImage;
}
func onSlideMenuButtonPressed(_ sender : UIButton){
if (sender.tag == 10)
{
// To Hide Menu If it already there
self.slideMenuItemSelectedAtIndex(-1);
sender.tag = 0;
let viewMenuBack : UIView = view.subviews.last!
UIView.animate(withDuration: 0.3, animations: { () -> Void in
var frameMenu : CGRect = viewMenuBack.frame
frameMenu.origin.x = -1 * UIScreen.main.bounds.size.width
viewMenuBack.frame = frameMenu
viewMenuBack.layoutIfNeeded()
viewMenuBack.backgroundColor = UIColor.clear
}, completion: { (finished) -> Void in
viewMenuBack.removeFromSuperview()
})
return
}
sender.isEnabled = false
sender.tag = 10
let menuVC : MenuViewController = self.storyboard!.instantiateViewController(withIdentifier: "MenuViewController") as! MenuViewController
menuVC.btnMenu = sender
menuVC.delegate = self
self.view.addSubview(menuVC.view)
self.addChildViewController(menuVC)
menuVC.view.layoutIfNeeded()
menuVC.view.frame=CGRect(x: 0 - UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height);
UIView.animate(withDuration: 0.3, animations: { () -> Void in
menuVC.view.frame=CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height);
sender.isEnabled = true
}, completion:nil)
}
}
MenuViewController
protocol SlideMenuDelegate {
func slideMenuItemSelectedAtIndex(_ index : Int32)
}
class MenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
/**
* Array to display menu options
*/
#IBOutlet var tblMenuOptions : UITableView!
/**
* Transparent button to hide menu
*/
#IBOutlet var btnCloseMenuOverlay : UIButton!
/**
* Array containing menu options
*/
var arrayMenuOptions = [Dictionary<String,String>]()
/**
* Menu button which was tapped to display the menu
*/
var btnMenu : UIButton!
/**
* Delegate of the MenuVC
*/
var delegate : SlideMenuDelegate?
override func viewDidLoad() {
super.viewDidLoad()
tblMenuOptions.tableFooterView = UIView()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateArrayMenuOptions()
}
func updateArrayMenuOptions(){
arrayMenuOptions.append(["title":"Locations", "icon":"LocationIcon"])
arrayMenuOptions.append(["title":"Offers", "icon":"OffersIcon"])
arrayMenuOptions.append(["title":"Feedback", "icon":"FeedbackIcon"])
arrayMenuOptions.append(["title":"About", "icon":"AboutIcon"])
arrayMenuOptions.append(["title":"Logout", "icon":"LogoutIcon"])
tblMenuOptions.reloadData()
}
#IBAction func onCloseMenuClick(_ button:UIButton!){
btnMenu.tag = 0
if (self.delegate != nil) {
var index = Int32(button.tag)
if(button == self.btnCloseMenuOverlay){
index = -1
}
delegate?.slideMenuItemSelectedAtIndex(index)
}
UIView.animate(withDuration: 0.3, animations: { () -> Void in
self.view.frame = CGRect(x: -UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width,height: UIScreen.main.bounds.size.height)
self.view.layoutIfNeeded()
self.view.backgroundColor = UIColor.clear
}, completion: { (finished) -> Void in
self.view.removeFromSuperview()
self.removeFromParentViewController()
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellMenu")!
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.layoutMargins = UIEdgeInsets.zero
cell.preservesSuperviewLayoutMargins = false
cell.backgroundColor = UIColor.clear
let lblTitle : UILabel = cell.contentView.viewWithTag(101) as! UILabel
let imgIcon : UIImageView = cell.contentView.viewWithTag(100) as! UIImageView
imgIcon.image = UIImage(named: arrayMenuOptions[indexPath.row]["icon"]!)
lblTitle.text = arrayMenuOptions[indexPath.row]["title"]!
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let btn = UIButton(type: UIButtonType.custom)
btn.tag = indexPath.row
self.onCloseMenuClick(btn)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayMenuOptions.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
}
a example of view controller where this happens
class LocationViewController: BaseViewController, CLLocationManagerDelegate, UITableViewDataSource, UITableViewDelegate {
var locItems:Array<LocItems>?
var locItemsWrapper:LocItemsWrapper?
var isLoadingLocItems = false
private var tb: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
//set up tableview
tb = UITableView()
//Set button to open up menu
self.addSlideMenuButton()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.locItems == nil) {
return 0
}
return self.locItems!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "locCell", for: indexPath) as! LocItemCell
if(self.locItems != nil && self.locItems!.count >= indexPath.row) {
let locItem = self.locItems![indexPath.row]
let rowsLoaded = self.locItems!.count
if (!self.isLoadingLocItems && (indexPath.row >= (rowsLoaded - rowsToLoadFromBottom))) {
let totalRows = self.locItemsWrapper!.count!
let remainingLocItemsToLoad = totalRows - rowsLoaded
if(remainingLocItemsToLoad > 0) {
self.loadMoreLocItems()
}
}
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if(self.locItems!.count >= indexPath.row) {
selectedLocId = self.locItems![indexPath.row].id!
selectedLocBg = self.locItems![indexPath.row].locationBackground!
//TODO: Set up Switch on api call
let parameters = [
"locId": selectedLocId
]
}
}
override func prepare(for segue: (UIStoryboardSegue!), sender: Any!) {
if(segue.identifier == "showCongratOffer") {
let svc = segue.destination as! CongratOfferViewController
svc.offerId = selectedLocId
svc.type = 1
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if((indexPath.row % 2) == 0) {
cell.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0)
} else {
cell.backgroundColor = UIColor.white
}
}
I know it's a lot of code, I tried to cut out pieces in the example where it happens as much as possible while leaving the menu code complete,
I would really appreciate if someone could help me with this issue or point me in the right direction.
A picture of the tableview before I open the menu,
A picture of the storyboard,
The top left controller can be ignored,
from login (top middle), we go to locations (bottom right, bottom middle) where the menu exists,
about (top right) will show the menu properly without the blank space,
the menu controller is bottom left.
The menu still works, though the empty space shows up in controllers with a UITableViewDataSource, UITableViewDelegate.
UPDATE: Looking at the view and how it gets built, I've found the menu height constraint is different. It's not getting set after menuVC.view.layoutIfNeeded() in BaseViewController.
print(self.childViewControllers[0].topLayoutGuide)
Empty Space Issue -> <_UILayoutGuide: 0x7fce6e7115e0; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x600000238700>>
Working -> <_UILayoutGuide: 0x7fce6e51ad30; frame = (0 0; 0 64); hidden = YES; layer = <CALayer: 0x60000023c9a0>>
How would you change just the height constraint on the view?
I can get the read-only view constraints like so,
self.childViewControllers[0].view.constraints
I see the issue now. What you need to do is copy the cell from your TableView, delete the original cell, make sure the TableView is completely empty, and paste the Cell back in. You can do this manually as well by making a new cell entirely. BTW, the issue is that there is an empty space above your cell which you need to remove from your TableView.

tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) Doesn't work when I add a UITapGestureRecognizer to my custom UITableViewCell

I have a very primitive problem at hand and that is when I have a tap gesture recognizer added to my custom cell I cant call the didSelectRowAt method.
My Code is the following:
import UIKit
class LanguageViewController: UIViewController {
#IBOutlet weak var titleLabel: AvenirUILabel!
#IBOutlet weak var backButton: UIButton!
#IBOutlet weak var submitButton: PinkUIButton!
#IBOutlet weak var addLanguageButton: UIButton!
#IBOutlet weak var addAnotherLanguageLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
var numberOfLanguagesSpoken = 2
var pickersStartHeight: CGFloat!
let downArrowImage = UIImage(named: "arrow_drop_down")
let BGColor = UIColor.white.withAlphaComponent(0.1)
var tag = 1001
var selectedCellHeight: CGFloat = 88.0
var unselectedCellHeight: CGFloat = 44.0
var selectedCellIndexPath: IndexPath?
let languages = ["Akan", "Amharic", "Arabic", "Assamese", "Awadhi", "Azerbaijani", "Balochi", "Belarusian", "Bengali", "Bhojpuri", "Burmese", "Cebuano", "Chewa", "Chhattisgarhi", "Chittagonian", "Czech", "Deccan", "Dhundhari", "Dutch", "Eastern Min", "English", "French", "Fula", "Gan Chinese", "German", "Greek", "Gujarati", "Haitian Creole", "Hakka", "Haryanvi", "Hausa", "Hiligaynon/Ilonggo", "Hindi ", "Hmong", "Hungarian", "Igbo", "Ilocano", "Italian", "Japanese", "Javanese", "Jin", "Kannada", "Kazakh", "Khmer", "Kinyarwanda", "Kirundi", "Konkani", "Korean", "Kurdish", "Madurese", "Magahi", "Maithili", "Malagasy", "Malay", "Malayalam", "Mandarin", "Marathi", "Marwari", "Mossi", "Nepali", "Northern Min", "Odia", "Oromo", "Pashto", "Persian", "Polish", "Portuguese", "Punjabi", "Quechua", "Romanian", "Russian", "Saraiki", "Serbo-Croatian", "Shona", "Sindhi", "Sinhalese", "Somali", "Southern Min", "Spanish", "Sundanese", "Swedish", "Sylheti", "Tagalog/Filipino", "Tamil", "Telugu", "Thai", "Turkish", "Turkmen", "Ukrainian", "Urdu", "Uyghur", "Uzbek", "Vietnamese", "Wu", "Xhosa", "Xiang ", "Yoruba", "Yue ", "Zhuang", "Zulu"]
var proficiency = ["Basic","Conversational","Proficient","Fluent","Native"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = backgroundTint
// initial set up
let height = self.view.bounds.height
titleLabel.textColor = .white
backButton.addTarget(self, action: #selector(goBack), for: .allTouchEvents)
tableView.dataSource = self
tableView.delegate = self
tableView.register(LanguageProficiencyTableViewCell.self, forCellReuseIdentifier: "Language")
tableView.backgroundColor = backgroundTint
tableView.allowsSelection = true
tableViewHeightConstraint.constant = 2*height/9
addLanguageButton.tintColor = .white
addLanguageButton.addTarget(self, action: #selector(addNewLanguage), for: .allTouchEvents)
addAnotherLanguageLabel.textColor = .white
submitButton.addTarget(self, action: #selector(submitChanges), for: .allEvents)
}
override func viewWillLayoutSubviews() {
unselectedCellHeight = self.view.bounds.height/9
selectedCellHeight = CGFloat(unselectedCellHeight * 2.0)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func goBack() {
self.dismiss(animated: true, completion: nil)
}
func submitChanges() {
self.dismiss(animated: true) {
//
}
}
func addNewLanguage() {
numberOfLanguagesSpoken += 1
UIView.animate(withDuration: 0.5) {
//
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
extension LanguageViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfLanguagesSpoken
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Language", for: indexPath) as! LanguageProficiencyTableViewCell
cell.parentVC = self
cell.backgroundColor = .clear
cell.languages = self.languages
cell.proficiency = self.proficiency
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedCellIndexPath == indexPath {
return selectedCellHeight
} else {
return unselectedCellHeight
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
//
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedCellIndexPath != nil && selectedCellIndexPath == indexPath as IndexPath {
selectedCellIndexPath = nil
} else {
selectedCellIndexPath = indexPath as IndexPath
}
tableView.beginUpdates()
tableView.endUpdates()
if selectedCellIndexPath != nil {
// This ensures, that the cell is fully visible once expanded
tableView.scrollToRow(at: indexPath as IndexPath, at: .none, animated: true)
}
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
return indexPath
}
}
import UIKit
class LanguageProficiencyTableViewCell: UITableViewCell {
let languageLabel = IndentedTextUILabel()
var languagePicker = UIPickerView()
weak var parentVC: LanguageViewController!
var cellHeight: CGFloat = 80
var open = false
var languages = [""]
var proficiency = [""]
var pickedLanguage = ""
var pickedProficiency = ""
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(languageLabel)
self.contentView.addSubview(languagePicker)
self.contentView.backgroundColor = .clear
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override func layoutSubviews() {
let width = self.bounds.width
let height = cellHeight
let BGColor = UIColor.white.withAlphaComponent(0.1)
languageLabel.frame = CGRect(x: 0.0, y: 1.0, width: width, height: height - 1.0)
languageLabel.textColor = .white
languageLabel.backgroundColor = BGColor
languageLabel.text = "Language(Proficiency)"
languageLabel.leftInset = 25.0
let downArrow = UIButton()
let downArrowImage = UIImage(named: "arrow_drop_down")
downArrow.setImage(downArrowImage, for: .normal)
downArrow.center.y = height/2
downArrow.center.x = 4 * width/5
languageLabel.addSubview(downArrow)
let tap = UITapGestureRecognizer(target: self, action: #selector(cellTapped))
self.addGestureRecognizer(tap)
languagePicker.frame = CGRect(x: 0.0, y: self.cellHeight + 1.0, width: self.bounds.width, height: 0.0)
languagePicker.delegate = self
languagePicker.dataSource = self
languagePicker.backgroundColor = UIColor.white.withAlphaComponent(0.05)
languagePicker.selectRow(20, inComponent: 0, animated: true)
let backGroundView = UIView(frame: CGRect(x: 0.0, y: 1.0, width: width, height: height - 1.0))
backGroundView.backgroundColor = .clear
self.selectedBackgroundView = backGroundView
}
func cellTapped() {
UIView.animate(withDuration: 0.2, animations: {
if !self.open {
self.languagePicker.frame.size.height = self.cellHeight - 2.0
} else {
self.languagePicker.isHidden = true
}
}, completion: { (_) in
if self.open {
self.languagePicker.frame.size.height = 0.0
self.languagePicker.isHidden = false
}
self.open = !self.open
})
}
}
It will not work because the tap gesture is taking the tap on itself and not sending the tap to the views underneath it.
You can make the tap gesture send the tap to be passed to the background views by
mySwipeGesture.cancelsTouchesInView = false
as described in How to pass a 'tap' to UIButton that is underneath UIView with UISwipeGestureRecognizer?
Also if you are using tap gesture just to dismiss your keyboard you can do so in didSelectAt method of the tableView and do any other thing after dismissing the keyboard in that method.
I'm doing something like what you have done and it's working for me.
You have to add the gesture recognizer not inside the TableViewCell class but from your Controller class when you set the cell.
Like this:
class LanguageViewController: UIViewController {
#IBOutlet weak var titleLabel: AvenirUILabel!
#IBOutlet weak var backButton: UIButton!
#IBOutlet weak var submitButton: PinkUIButton!
#IBOutlet weak var addLanguageButton: UIButton!
#IBOutlet weak var addAnotherLanguageLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
var numberOfLanguagesSpoken = 2
var pickersStartHeight: CGFloat!
let downArrowImage = UIImage(named: "arrow_drop_down")
let BGColor = UIColor.white.withAlphaComponent(0.1)
var tag = 1001
var selectedCellHeight: CGFloat = 88.0
var unselectedCellHeight: CGFloat = 44.0
var selectedCellIndexPath: IndexPath?
let languages = ["Akan", "Amharic", "Arabic", "Assamese", "Awadhi", "Azerbaijani", "Balochi", "Belarusian", "Bengali", "Bhojpuri", "Burmese", "Cebuano", "Chewa", "Chhattisgarhi", "Chittagonian", "Czech", "Deccan", "Dhundhari", "Dutch", "Eastern Min", "English", "French", "Fula", "Gan Chinese", "German", "Greek", "Gujarati", "Haitian Creole", "Hakka", "Haryanvi", "Hausa", "Hiligaynon/Ilonggo", "Hindi ", "Hmong", "Hungarian", "Igbo", "Ilocano", "Italian", "Japanese", "Javanese", "Jin", "Kannada", "Kazakh", "Khmer", "Kinyarwanda", "Kirundi", "Konkani", "Korean", "Kurdish", "Madurese", "Magahi", "Maithili", "Malagasy", "Malay", "Malayalam", "Mandarin", "Marathi", "Marwari", "Mossi", "Nepali", "Northern Min", "Odia", "Oromo", "Pashto", "Persian", "Polish", "Portuguese", "Punjabi", "Quechua", "Romanian", "Russian", "Saraiki", "Serbo-Croatian", "Shona", "Sindhi", "Sinhalese", "Somali", "Southern Min", "Spanish", "Sundanese", "Swedish", "Sylheti", "Tagalog/Filipino", "Tamil", "Telugu", "Thai", "Turkish", "Turkmen", "Ukrainian", "Urdu", "Uyghur", "Uzbek", "Vietnamese", "Wu", "Xhosa", "Xiang ", "Yoruba", "Yue ", "Zhuang", "Zulu"]
var proficiency = ["Basic","Conversational","Proficient","Fluent","Native"]
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = backgroundTint
// initial set up
let height = self.view.bounds.height
titleLabel.textColor = .white
backButton.addTarget(self, action: #selector(goBack), for: .allTouchEvents)
tableView.dataSource = self
tableView.delegate = self
tableView.register(LanguageProficiencyTableViewCell.self, forCellReuseIdentifier: "Language")
tableView.backgroundColor = backgroundTint
tableView.allowsSelection = true
tableViewHeightConstraint.constant = 2*height/9
addLanguageButton.tintColor = .white
addLanguageButton.addTarget(self, action: #selector(addNewLanguage), for: .allTouchEvents)
addAnotherLanguageLabel.textColor = .white
submitButton.addTarget(self, action: #selector(submitChanges), for: .allEvents)
}
override func viewWillLayoutSubviews() {
unselectedCellHeight = self.view.bounds.height/9
selectedCellHeight = CGFloat(unselectedCellHeight * 2.0)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func goBack() {
self.dismiss(animated: true, completion: nil)
}
func submitChanges() {
self.dismiss(animated: true) {
//
}
}
func addNewLanguage() {
numberOfLanguagesSpoken += 1
UIView.animate(withDuration: 0.5) {
//
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
extension LanguageViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfLanguagesSpoken
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Language", for: indexPath) as! LanguageProficiencyTableViewCell
cell.parentVC = self
cell.backgroundColor = .clear
cell.languages = self.languages
cell.proficiency = self.proficiency
let tap = UITapGestureRecognizer(target: self, action: #selector("cellTapped:"))
cell.addGestureRecognizer(tap)
return cell
}
//Change here you can't call languagePicker with self but you have to get it from the object tapped that in your case is you cell
func cellTapped(recognizer: UITapGestureRecognizer) {
UIView.animate(withDuration: 0.2, animations: {
if !self.open {
self.languagePicker.frame.size.height = self.cellHeight - 2.0
} else {
self.languagePicker.isHidden = true
}
}, completion: { (_) in
if self.open {
self.languagePicker.frame.size.height = 0.0
self.languagePicker.isHidden = false
}
self.open = !self.open
})
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedCellIndexPath == indexPath {
return selectedCellHeight
} else {
return unselectedCellHeight
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
//
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedCellIndexPath != nil && selectedCellIndexPath == indexPath as IndexPath {
selectedCellIndexPath = nil
} else {
selectedCellIndexPath = indexPath as IndexPath
}
tableView.beginUpdates()
tableView.endUpdates()
if selectedCellIndexPath != nil {
// This ensures, that the cell is fully visible once expanded
tableView.scrollToRow(at: indexPath as IndexPath, at: .none, animated: true)
}
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
return indexPath
}
}
import UIKit
class LanguageProficiencyTableViewCell: UITableViewCell {
let languageLabel = IndentedTextUILabel()
var languagePicker = UIPickerView()
weak var parentVC: LanguageViewController!
var cellHeight: CGFloat = 80
var open = false
var languages = [""]
var proficiency = [""]
var pickedLanguage = ""
var pickedProficiency = ""
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(languageLabel)
self.contentView.addSubview(languagePicker)
self.contentView.backgroundColor = .clear
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override func layoutSubviews() {
let width = self.bounds.width
let height = cellHeight
let BGColor = UIColor.white.withAlphaComponent(0.1)
languageLabel.frame = CGRect(x: 0.0, y: 1.0, width: width, height: height - 1.0)
languageLabel.textColor = .white
languageLabel.backgroundColor = BGColor
languageLabel.text = "Language(Proficiency)"
languageLabel.leftInset = 25.0
let downArrow = UIButton()
let downArrowImage = UIImage(named: "arrow_drop_down")
downArrow.setImage(downArrowImage, for: .normal)
downArrow.center.y = height/2
downArrow.center.x = 4 * width/5
languageLabel.addSubview(downArrow)
languagePicker.frame = CGRect(x: 0.0, y: self.cellHeight + 1.0, width: self.bounds.width, height: 0.0)
languagePicker.delegate = self
languagePicker.dataSource = self
languagePicker.backgroundColor = UIColor.white.withAlphaComponent(0.05)
languagePicker.selectRow(20, inComponent: 0, animated: true)
let backGroundView = UIView(frame: CGRect(x: 0.0, y: 1.0, width: width, height: height - 1.0))
backGroundView.backgroundColor = .clear
self.selectedBackgroundView = backGroundView
}
}

Resources