UITableViewCell custom class - Reload cell height after change in subview height constraint - ios

I have a button in custom UITableViewCell class. It shows/hides a view (part of same cell). The height of cell should change on doing that.
This is the button action (in UITableViewCell custom class):
#IBAction func showHideCartView(sender: UIButton)
{
if sender.tag == 1
{
// Show cart view
self.buttonArrow.tag = 2
self.viewAddToCart.isHidden = false
self.constraint_Height_viewAddToCart.constant = 50
self.buttonArrow.setImage(UIImage(named: "arrowUp.png"), for: .normal)
}
else
{
// Hide cart view
self.buttonArrow.tag = 1
self.viewAddToCart.isHidden = true
self.constraint_Height_viewAddToCart.constant = 0
self.buttonArrow.setImage(UIImage(named: "arrowDown.png"), for: .normal)
}
self.setNeedsUpdateConstraints()
self.setNeedsLayout()
self.layoutIfNeeded()
}
The height of cell remains unchanged. It is only when I scroll the UITableView and revisit the cell, it's height gets updated.

Add the following method to the viewController with the tableView to refresh the table when a cell is expanded:
func refreshTableAfterCellExpansion() {
self.tableView.beginUpdates()
self.tableView.setNeedsDisplay()
self.tableView.endUpdates()
}
Call the method after the constraints have been updated. If the button manipulates the constraints inside the cell, you will need to notify the viewController about the change so that you can call the method. Either use a delegate pattern (or pass directly a tableView reference to each expandable cell - just remember to store it as weak variable), or post a notification using NotificationCenter.default.

As this code is in the TableViewCell, You need to make a action method for your cell in the TableViewController so when the button is clicked you would know. You can keep a reference of your button in your tableviewCell and then inside the cell cellForRowAt you can do
cell.yourButtonRef.addTarget(self, action: #selector(expandButtonTapped(_:)), for: .touchDown)
and the make a function called "expandButtonTapped" and add the tableview beign and end update there
#objc func expandButtonTapped(_ button: UIButton) {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
Please let me know if this has worked or not

Related

UITextField in UITableViewCell and becomeFirstResponder on selected cell keyboard show/hide glitch

I have such view hierarchy:
View Controller
UITableView
UITableViewCell
UITextField
I have in my custom UITableViewCell a textField and my plan is to make this textField becomeFirstResponder as soon as user tap on cell. The first problem is that textField is getting touches events and cell is not selected because of that so when user tap right onto the textField then cell is not selected and I'm not able to update selected cell UI. I though that I can make isUserInteractionEnabled=false on textField so after user taps on textField I will have proper selection behavior in my cells. So far so good. Now I want to handle this selection in setSelected method in my custom cell. The problem is that if keyboard is already on screen and I execute becomeFirstResponder() on textField then the keyboard is hiding and immediately showing again. Normally I expect that this keyboard will stay on its place without trying to hide and show again. Here is the code:
class StringFieldTableViewCell: UITableViewCell {
#IBOutlet weak var pinLine: UIView!
#IBOutlet weak var pinLineFocused: UIView!
#IBOutlet weak var textField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
self.selectionStyle = .none
self.textField.isUserInteractionEnabled = false
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
self.textField.isUserInteractionEnabled = selected
if selected {
self.textField.becomeFirstResponder()
}
self.pinLine.isHidden = selected
self.pinLineFocused.isHidden = !selected
}
}
Does anybody knows how to fix this keyboard showing/hiding problem and why keyboard tries first to hide itself and then show in very short time?
The problem in your code is that you are kind of disabling the textField interaction because you have textField in your cells and if you tap on the cell you will interact with the textField, not the cell itself.
So there is a way to select a row even if you have a textField in your cell.
First, you need to get when you tapped on that textField.
for that you can use this....
let pointInTable = textField.convert(textField.bounds.origin, to: self.tableView)
It will give you a value in a CGPoint([x, y]), for E.g [0.0, 0.0] or [0.0, 44.0]. Depending upon the height of your Cell.
Now, there is a function that converts CGPoint to IndexPath.
let textFieldIndexPath = tableView.indexPathForRow(at: pointInTable)
Now this will be our indexPath.
Now you can use cellSelected Fuction.
tableView.selectRow(at: textFieldIndexPath, animated: true, scrollPosition: .none)
The whole code will be like this:
let pointInTable = textField.convert(textField.bounds.origin, to: self.tableView)
let textFieldIndexPath = self.tableView.indexPathForRow(at: pointInTable)
tableView.selectRow(at: textFieldIndexPath, animated: true, scrollPosition: .none)
You can put this in textFieldDidBeginEditing Function.

collectionViewCells interact

I have a collection view and each cell has a button,
I added an IB-action that is called when the button is pressed.
My issue is that when a certain button is tapped I want to change not only the background color of that button, I want to change all buttons in all the cells
I'm not sure how to implement this...
Thanks.
Try Somthg like this
Take a var selectedIndex : Int = -1
in cellForItemAt
cell.button.tag = indexpath.item
if selectedIndex == indexPath.item{
cell.button.backgroundColor = UIColor.blue // New Color
}
else{
cell.button.backgroundColor = UIColor.gray // Deafult color
}
in Button Action
selectedIndex = sender.tag
collectionView.ReloadData()
When you pressed to the button you should notify your collectionView class holder (by delegate pattern or using a closure) that right now all buttons should be changed. Then you should force change all visible buttons (take all visibleCells of collectionView and change the buttons style) and also in the cellForItemAtIndexPath method change the button style for every other buttons which is invisible right now.

Hide/Show a View dragged above tableviewcell in Swift 3

I have a tableView, from storyboard i have dragged a UIView just above cell within tableView.So i want to Hide/Show that view on button Action which is outside tableView. Another thing is when i hide that view on button Action tableView will scroll Up and when i show that view on button Action tableView will scroll down,Anyhelp will be appreciated, thanks in advance
That can be done via two ways.
i) Either you can create a protocol in uitableviewCell class and implement in MasterClas which is having your tableview.
ii) Or you can directly call a function which will hide/show your view in MasterClass(Which is having your tableview) in cell for row at index. Like below
cell.button.addTarget(self, : "MasterClassFunction:", forControlEvents: .touchUpInside)
Both function will work. And for top and down animation. Just calculate your view's height and change Y axis as per your requirement On function call with a animation block.
Hope this will help.
Okay here i am editing my answer for some code reference.
`protocol tableViewCellDelegate {
func buttonAction()
}
#IBOutlet weak var cellButton: UIButton!
in awakefrom nib :
`cell.button.addTarget(self, : "buttonActionCell:", forControlEvents: .touchUpInside)
func buttonActionCell(){
delegate.buttonAction()
}
And in your masterClass assign delegate to controller's reference and call the protocol function in your main class
func buttonAction(){
print("Func called in class file")
}
`
First you need to create an outlet for the height of the UIView that you want to hide. give the vertical spacing of the tableview to zero with that UIView.
Then on the Button action you need to give the code for the height constraint of the UIView to be set to zero.

How can I identify a table row in iOS?

I made a prototype cell and I have data from a database to load into the cells in a table.
Each cell has a label and 3 buttons just like this:
If I click on a button for example "Meets Standard", how can I identify in which row I tapped the button?
So for example when I press the "Meets Standard" button at a given row I'd like to change the background color of that row to red. How can I do it?
I have a CustomCell.swift class where I configure the prototype cell and a TableView.swift class where I configure the table.
Try control dragging the button to your CustomCell.swift class and typing a name and selecting "Action" on the popup menu. Then, inside the generated method, you can call self.backgroundColor = UIColor.redColor() or perform any other operations that you'd like.
Edit:
Here's what I think you should do to change the height:
Make a boolean in your cell class called "expanded" or something. Then, go into your table view class and implement the heightForRowAtIndexPath method. In that method, retrieve the cell and check if it's expanded, and if so, return a larger height. Now, to make it reload, you will need to store a reference to the table view in each cell, as it says here: Reference from UITableViewCell to parent UITableView?
In that clicked method you already made, where the background is set to red, you will need to call:
tableView.beginUpdates()
tableView.endUpdates()
where tableview is the weak reference you already stored. Also, in that method, you need to add self.expanded = true of course.
Edit 2:
You know what, maybe it's easier to just do this:
weak var _tableView: UITableView!
...
func tableView() -> UITableView! {
if _tableView != nil {
return _tableView
}
var view = self.superview
while view != nil && !(view?.isKindOfClass(UITableView))! {
view = view?.superview
}
self._tableView = view as! UITableView
return _tableView
}

UIButton not responding used in a custom UITableViewCell

I know this issue is already been asked few times in SO. Despite trying those out, I am still unable to solve my problem.
I am using a UITableView inside a UIViewController. I have a custom UITableViewCell which has couple of buttons in it. However, I am not able to make the Button respond to Click event.
The development environment is iOS 9 and Swift 2
Snippets used :
BranchNearMeTableViewCell.swift contains
#IBOutlet weak var btnDetails: UIButton!
view controller class
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("branchNearTableCell") as! BranchNearMeTableViewCell
cell.btnDetails.tag = indexPath.row
cell.btnDetails.addTarget(self, action: "showDetails:", forControlEvents: .TouchUpInside)
}
func showDetails(sender: UIButton){
print("Button Pressed:")
}
Additional Info:
TableView and TableCellView has User interaction disabled in Interface builder since don't want the entire cell to be clickable.
UIButton inside TableViewCell has User Interaction enabled.
Being an iOS noob, I may be making a silly mistake which I might have overlooked.
Similar questions that I checked include:
SO1
SO2
SO3
I Deeply appreciate any help regarding this question.
I faced a similar issue. I was programmatically adding an UIButton to the UITableViewCell via addSubview. The button would not respond to touch events. Using Debug View Hierarchy, I finally discovered that any subviews added to the UITableViewCell was behind contentView, which was blocking user input from reaching the UIButton. The issue was resolved by adding the UIButton to contentView instead of the UITableViewCell.
I would have userInteractionEnabled set to true on the table view cell as well. I would prevent taps using the UITableView allowsSelection to false
Also remember to remove the target and action in tableView:cellForRowAtIndexPath: since the cells are recycled, the button might already have the target and action, it might add a second.
I found a simple solution:
Inherits UITableViewCell, and override init()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
//init subviews, eg. self.switch = UISwitch()
super.init(style: style, reuseIdentifier: reuseIdentifier)
// add this line magic code
contentView.isUserInteractionEnabled = true
//add subviews, e.g. self.addSubView(self.switch)
}
You only have to do (in ViewDidLoad):
mTableView.delaysContentTouches = false
For programmatically created views, the only thing to remember is to declare buttons using lazy var in UITableViewCell. And also add subviews to contentView instead of the cell itself For example:
class CounterCell: UITableViewCell {
lazy var incrementButton: UIButton = {
let button = UIButton()
button.setTitle("+", for: .normal)
button.addTarget(self, action: #selector(incrementAction), for: .touchUpInside)
return button
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(incrementButton)
// Your constrains here
}
#objc func incrementAction() {
}
}
When using programmatically views, there's no need to add .userInteractionEnabled flags.
Then to take the action out of the cell, just add a delegate and assign it from the UITableViewDataSource.
I came across this issue today, with a button inside a static UITableview cell, that was not responding to user events.
I realised the 'Content View' of the cell also has a 'User Interaction Enabled' tick box. Make sure you select the 'Content View' inside the UITableview cell in your Document Outline menu, then tick the box for 'User Interaction Enabled' in the Attributes Inspector - see attached photo for reference. 'User Interaction Enabled' also needs to be checked for the cell for this to work.
Hope this helps. XCcode screen shot
Also, make sure you are adding target actions to your buttons outside their setup. So instead of
let button: UIButton = {
//addTarget...
}()
you can have a function to set up your buttons after something happens:
func setButtonsUp() {
// myButton.addTarget
}
For anyone else struggling, here's my solution:
sendSubviewToBack(cell.contentView)
The thing that there's now an extra UITableViewCellContentView layer which blocks interaction with views behind it.
Related issue: An extra UITableViewCellContentView overlay appears in a TableView on iOS 14 preventing taps, but works fine on iOS 13
Ad a first sight nothing seems to be wrong with your code.
So I suggest you to add a background color to the superview of the button, why? because if the button is outside the frame of its superview it will never receive touches.
If you see that the button is not inside the background color probably you have an issue positioning the item, check constraints or whatever you are using.
Check also the frame of the button.
You can also do both by inspecting the view at runtime, here a tutorial.
I dont know what wrong in the code but i can suggest which i personally use and it works for me
In BranchNearMeTableViewCell.swift
#IBOutlet var btnDetails: UIButton!
#IBAction func btnDetailsClick(sender: AnyObject) {
tapButton?(self)
}
var tapButton: (UITableViewCell -> Void)?
In Table view controller
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("branchNearTableCell") as! BranchNearMeTableViewCell
cell.tapButton = {(user) in
//User will be tablecell here do whatever you want to do here
}
}
So if you click on button in table cell this cell.tapButton will be called you can do whatever you want to do here
The only things we need to do is in cellForRowAt just put:
cell.selectionStyle = .none
in this way, UITableview will bypass the touch of selecting cells and allow buttons inside our cells to be clickable.
set cell and cell content view isUserInteractionEnabled = true
Add Tapgesture to the button
Add a closure to handle gesture action
Add target for that button.
button.addTarget(self, action: #selector(connected(sender:)), for: .touchUpInside)
Set tag of that button since you are using it.
button.tag = indexPath.row
Achieve this by subclassing UITableViewCell. button on that cell, connect it via outlet.
Make sure button.isUserInteractionEnabled = true
To get the tag in the connected function:
#objc func connected(sender: UIButton){
let buttonTag = sender.tag
}
Make sure that ALL of tableView's superviews do have isUserInteractionEnabled set to true
User interaction was already enabled for my UIButton. The thing that worked for me is
switching the stackView distribution to "Fill".

Resources