tableViewCell image property reset when selected - swift - swift

I do create a custom cell with 2 UIImageView inside and a UILabel this way:
let ChildCellID = "ChildCell"
if indexPath.section < 2 {
var cell = SectionCell.loadOrDequeueWithTableView(tableView, reuseIdentifier: SectionCellID)
if cell == nil {
cell = SectionCell.viewFromNib()
}
cell.delegate = self as SectionCellDelegate
cell.avatar?.loadAvatarURL(child.avatarUrl)
cell.avatar.layer.cornerRadius = cell.avatar.frame.size.width / 2
The attribut avatar is the UIImageView I decided to round. When a cell is selected my code goes:
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
let tableViewCell = tableView.cellForRowAtIndexPath(indexPath)
if let cell : SectionCell = tableViewCell as? SectionCell {
cell.selection = !cell.selection
} else if let cell : ChildCell = tableViewCell as? ChildCell {
cell.selection = !cell.selection
}
return indexPath
}
Selection is the checkbox as UIImageView. My problem is when I select a cell, the cell.avatar loses his property meaning it goes back as a square form. The cell.avatar is still loaded and I tried cell.avatar.layer.cornerRadius = cell.avatar.frame.size.width / 2in the willSelectRowAtIndexPath and didSelectRowAtIndexPath but without success.
Am I missing anything that makes my avatar loses his rounded property ?
The only thing that could possibly be the origin of this problem would be the fact that I do use the checkbox UIImageView which is really near my avatar.

I found out that the checkbox near my avatar lacked a constraints that, for some random reason and even though it did not impact the avatar view, was resetting the avatar view rounded property.

Related

Current coordinates of a tableViewCell swift

fellows!
I'm trying to make an animation of an image in my tableViewCell. My intent is to get an imageView that is in a tableViewCell, add it to exactly the same place in a view, and then use scale animation. Currently, my code looks as below, however, I need to find a cell center point in every period of time (after scrolling too), that I can add image view exactly in the same place. The value of cellRect.origin.y constantly increases, whereas I need it to be exactly center.y point of my tableView cell.
Could you please tell me where my mistake is?
Thank you in advanse!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
cell.animateImageView = { [weak self]
guard let self = self else { return }
tableView.layoutSubviews()
var cellRect = self.view.convert(tableView.rectForRow(at: indexPath), to: self.tableView)
cellRect.origin.x = UIScreen.main.bounds.midX - (cell.photoImageView.frame.width / 2)
cellRect.origin.y = cellRect.origin.y + (cell.photoImageView.frame.height / 2)
UIView.animate(withDuration: 3) {
} completion: { isFinished in
}
}
}
If I understand you correctly, this is your goal
you tap on a UITableViewCell which has an imageView
You want to create a new image view with the same image in the same position as the image in the cell you tapped
Then from this position you will make this image full screen with animation
You want to do something after the animation has completed
If yes, there here are my thoughts
I feel this line has the first issue:
var cellRect
= self.view.convert(tableView.rectForRow(at: indexPath),
to: self.tableView)
Let's take a look at the convert function in the docs
Converts a rectangle from the receiver’s coordinate system to that of another view.
Parameters
rect
A rectangle specified in the local coordinate system
(bounds) of the receiver.
view
The view that is the target of the conversion operation. If view
is nil, this method instead converts to window base coordinates.
Otherwise, both view and the receiver must belong to the same UIWindow
object.
The receiver here is your cell's image view which you want to recreate so you should not call self.view.convert, it should be cell.photoImageView.convert
The rect parameter is the bounds of your tapped cell's image view and the to parameter is self.view as you want to convert the image view's coordinates in the cell to the coordinates in the main view of the view controller.
Beyond that I am not sure of the purpose of these as you will get the frame from the above function so I removed them but you can add it back if it makes sense to your application.
cellRect.origin.x
= UIScreen.main.bounds.midX - (cell.photoImageView.frame.width / 2)
cellRect.origin.y
= cellRect.origin.y + (cell.photoImageView.frame.height / 2)
So this is how I changed the function:
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath)
{
// Retrieved the actual cell that was tapped
// same as what you did
let cell
= tableView.cellForRow(at: indexPath) as! TableViewCell
// Here I made a change based on my explanation above
let cellRect =
cell.photoImageView.convert(cell.photoImageView.bounds,
to: view)
// I created a blue view just for demo
let aView = UIView(frame: cellRect)
aView.backgroundColor = .blue
view.addSubview(aView)
}
If you end it here, you will get this result
The blue view added perfectly where we want it
Now to complete the rest with animation is quite simple after we accomplished the tricky part and here is the full function
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath)
{
// Retrieved the actual cell that was tapped
let cell
= tableView.cellForRow(at: indexPath) as! TableViewCell
// Here I made a change based on my explanation above
let cellRect =
cell.photoImageView.convert(cell.photoImageView.bounds,
to: view)
let animateImageView = UIImageView(frame: cellRect)
animateImageView.image = cell.photoImageView.image
animateImageView.clipsToBounds = true
view.addSubview(animateImageView)
UIView.animate(withDuration: 2.0)
{ [weak self] in
if let strongSelf = self
{
animateImageView.frame = strongSelf.view.bounds
}
} completion: { (success) in
if success
{
// animation completed, do what you want
// like push your VC
}
}
}
The end result I believe is what you want
I hope this helps you get you closer to your desired result

adding Button in last tableView Cell shown twice when scrolling

Using Swift4, iOS11.2.1, Xcode9.2,
I successfully added a custom button to the last cell of a tableView. (the button is used to add cells in the tableView - this also works fine...) - see Screenshot-1.
But now the issue: When adding more cells (i.e. more than fit in the tableView-height), and if you scroll to the top, then there is a second cell that shows this button - see Screenshot-2.
(see code below....)
Here are the two screenshots:
How can I get rid of the extra button ????
(it seems that a cell is reused and the button is partly drawn. The cut-off can be explained since the height of the last cell is made bigger than the other cells. But still, there shouldn't be parts of buttons in any other cell than the very last one...even when scrolling up).
Any help appreciated !
Here is my code (or excerts of it...):
override func viewDidLoad() {
super.viewDidLoad()
// define delegates
self.fromToTableView.dataSource = self
self.fromToTableView.delegate = self
// define cell look and feel
self.addButton.setImage(UIImage(named: "addButton"), for: .normal)
self.addButton.addTarget(self, action: #selector(plusButtonAction), for: .touchUpInside)
//...
self.fromToTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let resItems = resItems else {
self.rowCount = 0
return 0
}
self.rowCount = resItems.count - 1
return resItems.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if(indexPath.row == (self.rowCount)) {
return 160
} else {
return 120
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = fromToTableView.dequeueReusableCell(withIdentifier: "SettingsCell") as? SettingsCustomTableViewCell
// configure the cell
cell?.configureCell(tag: indexPath.row)
// fill cell-data
if let resItem = self.resItems?[indexPath.row] {
cell?.connectionItem = resItem
}
// add addButton to last cell
if(indexPath.row == (self.rowCount)) {
// add addButton
cell?.contentView.addSubview(self.addButton)
// scroll to last cell (that was just added)
// First figure out how many sections there are
let lastSectionIndex = self.fromToTableView.numberOfSections - 1
// Then grab the number of rows in the last section
let lastRowIndex = self.fromToTableView.numberOfRows(inSection: lastSectionIndex) - 1
// Now just construct the index path
let pathToLastRow = IndexPath(row: lastRowIndex, section: lastSectionIndex)
// Scroll to last cell
self.fromToTableView.scrollToRow(at: pathToLastRow as IndexPath, at: UITableViewScrollPosition.none, animated: true)
}
return cell!
}
You already figured out what the problem is :)
As you say here:
it seems that a cell is reused and the button is partly drawn
And that is exactly what happens here. You have a pool of UITableViewCell elements, or in your case SettingsCustomTableViewCell. So whenever you say:
let cell = fromToTableView.dequeueReusableCell(withIdentifier: "SettingsCell") as? SettingsCustomTableViewCell
Then, as the name implies, you dequeue a reusable cell from your UITableView.
That also means that whatever you may have set on the cell previously (like your button for instance) stays there whenever you reuse that specific instance of a cell.
You can fix this in (at least) three ways.
The Hack
In your tableView(_:cellForRowAt:indexPath:) you have this:
if(indexPath.row == (self.rowCount)) {
//last row, add plus button
}
You can add an else part and remove the plus button again:
if(indexPath.row == (self.rowCount)) {
//last row, add plus button
} else {
//not last row, remove button
}
The Right Way
UITableViewCell has the function prepareForReuse and as the documentation says:
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCell(withIdentifier:)
So, this is where you "reset" your custom table view cell. In your case you remove the plus button.
You already have a custom UITableViewCell it seems, so this should be a small change.
The Footer Way
As #paragon suggests in his comment, you can create a UIView and add that as a tableFooterView to your UITableView.
let footerView = YourFooterViewHere()
tableView.tableFooterView = footerView
Hope that helps

Set action for subview inside a table cell (swift)

When i clicked the subview,it didn't trigger subview's action. But i have selected whole cell. How to fix it?
Here is my code.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("idcell", forIndexPath: indexPath) as UITableViewCell
let lblTitle : UILabel = cell.contentView.viewWithTag(101) as! UILabel
lblTitle.text = (deptId[indexPath.row] as? String)! + " " + (deptDesc[indexPath.row] as? String)!
var height:CGFloat = 40
if(indexPath.row == departmentSelectedRow){
for i in 0...deptProfile.count-1{
let label = UILabel(frame: CGRectMake(0,height,400,30))
label.targetForAction("sadasdd", withSender: nil)
height = height+40
label.text = ("ewrewrewre")
label.backgroundColor = UIColor.redColor()
cell.addSubview(label)
}
}
return cell
}
If you want to interact with something inside a cell (rather than select the whole cell) then you should use a button or a gesture recogniser. These are properly interactive elements that you can add a target and action to.
You should also change how you're using cells, specifically:
Use custom cell classes, so you can directly reference the cell elements instead of using viewWithTag which is bad practice
Use multiple different cell classes (and thus identifiers) so you can use actually different cells for selected and unselected rows
Don't create and add subviews on the fly, this is what the different cell classes are for

Change textLabel and imageVIew order in a cell in Swift

I'm putting an image and a text label in the same table cell. I want to make the text label followed by the image but the image comes first. Here is the code.
cell.textLabel?.text = "Text label comes here"
let image = UIImage(named: ChangeYTDArrow!)
var imageView = UIImageView(image: image!)
cell.imageView!.image = image
And here is what happens withe the code.
Thank you in advance.
You should probably use a custom tableViewCell in the interface builder, it allows you to everything you want in a tableViewCell like multiple UILabel or everything you want.
To do so, go in your storyboard, select your tableView then on the tableView property set the field Prototype Cells to 1, a new cell will appears and you will be able to design what you want.
Add an identifier to this cell then create a new class for your cell and add your #IBOutlet, here you should have multiple label & an UIImageView()
Then in your controller with textView method, at the cellForRowAtIndexPath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let reuseIdentifier = "yourCellIdentifier"
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as? yourTableViewCell ?? GolfCourseTableViewCell(style: .Default, reuseIdentifier:yourCellIdentifier)
cell.yourLabel1.text = "blahblah"
cell.yourLabel2.text = "blablahbl"
let image = UIImage(named: ChangeYTDArrow!)
var imageView = UIImageView(image: image!)
cell.imageView!.image = image
return cell
}

problems with UITableView and reuseablecells

so i am experiencing two problems right now with my (pretty basic) tableview. i will paste my code below but here is the general gist: i'm making a dictionary app, so each cell contains two labels for the two languages. if a cell gets tapped, i'd like for: 1. cell height increase, 2. font size increase, 3. text align center, 4. cell background change color.
i've also included code to 'reset' a selected cell if it is selected a second time - this seems to be causing my second problem
the problem is when a cell is selected:
those four things happen to cells which are currently offscreen.
the program crashes when: i select cell 0, the cell changes which is fine, but when i click on any cell that is offscreen at the time (say cell 10) the program will crash giving me this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
code:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// reset previous cell but only if selectedRow isn't -1 which is initial value
if selectedRowIndex != -1{
let previousCell = convoTable.cellForRowAtIndexPath(NSIndexPath(forRow: selectedRowIndex, inSection: 0)) as! PhraseTableViewCell
previousCell.backgroundColor = UIColor.whiteColor()
previousCell.lang2Label.font = UIFont(name: (previousCell.lang2Label?.font?.fontName)!, size: (previousCell.lang2Label?.font?.pointSize)! - 5)
previousCell.lang2Label.textAlignment = .Right
}
//make changes to new cell that was pressed upon
if selectedRowIndex != indexPath.row{
print("new cell got selected")
cellSelected = true
// save the selected index
self.selectedRowIndex = indexPath.row
let cell = self.convoTable.cellForRowAtIndexPath(indexPath) as! PhraseTableViewCell
cell.lang2Label.font = UIFont(name: (cell.lang2Label?.font?.fontName)!, size: (cell.lang2Label?.font?.pointSize)! + 5)
cell.backgroundColor = UIColor.cyanColor()
cell.lang2Label.textAlignment = .Center
// update the height for all the cells
self.convoTable.beginUpdates()
self.convoTable.endUpdates()
cell.lang1Label.frame = CGRectMake(3,0, cell.bounds.width, cell.bounds.height/3)
cell.lang2Label.frame = CGRectMake(-3,cell.bounds.height/3, cell.bounds.width, cell.bounds.height/3 * 2)
}
else {
print("same cell pressed")
cellSelected = false
selectedRowIndex = -1
}
self.convoTable.beginUpdates()
self.convoTable.endUpdates()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! PhraseTableViewCell
cell.lang1Label.text = items![indexPath.row].eng
cell.lang2Label.text = items![indexPath.row].prs
return cell
}
thank you all very much, this help is much appreciated.
so there is no real answer to the questions i posed, the best workaround is to just have another array which maintains the states in the cells. sucks i know but really the best "solution"