Swift collectionView select cell multiple selection - ios

I have a UICollectionView with 2 sections. I want to select the cell when the user taps on it.
My code runs correctly every time a user taps on the cell, the cell become smaller and a checkmark appears in it ( it's the imageView I add as subview of the cell). The problem is that if I tap a cell on the first section, it selects another cell in the second section. This is weird as I use the indexPath.
This is my code:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// handle tap events
let cell = collectionView.cellForItemAtIndexPath(indexPath)
let centerCell = cell?.center
if cell!.frame.size.width == cellWidth {
cell?.frame.size.width = (cell?.frame.size.width)!/1.12
cell?.frame.size.height = (cell?.frame.size.height)!/1.12
cell?.center = centerCell!
let imageView = UIImageView()
imageView.image = MaterialIcon.check?.imageWithColor(MaterialColor.white)
imageView.backgroundColor = MaterialColor.blue.accent2
imageView.frame = CGRectMake(1, 1, 20, 20)
imageView.layer.cornerRadius = imageView.frame.height/2
imageView.clipsToBounds = true
if indexPath.section == 0 {
imageView.tag = indexPath.row+4000
} else {
imageView.tag = indexPath.row+5000
}
print("IMAGEVIEW TAG: ",imageView.tag)
cell?.addSubview(imageView)
}
}

Be sure to have the multiple selection property on collectionView set to true in your viewDidLoad() or in storyboard
collectionView?.allowsMultipleSelection = true

Related

UITableViewCell subviews are displaying outside of cell

In a section of my UITableView, there are 5 cells, three of which have been configured to expand/collapse to provide a more detailed view when selected. One of these cells shows a diagram of a number of small squares, which displays perfectly, until another cell is expanded, like this:
When the cell is collapsed, however, the subviews in the cell display in different cells, in different sections, like this:
and this:
To create the subviews in the cell, this is my code in the cellForRow method, which just uses an array of UIViews:
for vote in vote_array {
cell.contentView.addSubview(vote as? UIView ?? UIView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0)))
}
I tried removing all the subviews before I added them by doing this, but it doesn't change anything:
for subview in cell.contentView.subviews {
subview.removeFromSuperview()
}
Edit: This is inside a switch statement, but here is the relevant cell/case cellForRow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.contentView.clipsToBounds = true
cell.clipsToBounds = true
let vote_array = getVoteArray()
for subview in cell.contentView.subviews {
subview.removeFromSuperview()
}
for case let vote as UIView in vote_array {
cell.contentView.addSubview(vote)
}
Edit:
The core of getVoteArray:
func getVoteArray() -> NSMutableArray {
var i = 0
var x = 20
var y = 4
let blockViews : NSMutableArray = []
for color in blocks {
let block = UIView.init(frame: CGRect.init(x: x, y: y, width: 20, height: 20))
block.backgroundColor = color as? UIColor
blockViews.add(block)
x = x + 24
i = i + 1
if i == num_blocks_per_row { i = 0; y = y + 24; x = 20 }
}
diagramHeight = y + 24
return blockViews
}
I can't seem to figure out why the subviews are generating randomly all over the tableView.
Ended up adding
for case let cell as UITableViewCell in tableView.subviews {
for subview in cell.contentView.subviews {
if subview.tag == 115 {
subview.removeFromSuperview()
}
}
}
to my didSelectRowAt method, after adding the tag when each view is created. I'm still not sure why the views were being added to different cells, but this got rid of them at least.
Try to implement unique ReuseIdentifiers for collapsed and expanded states.
If the cell is collapsed then don't load all those views in it by dequeuing a collapsedCell where the height of all those UIViews is either 0 or they are not added to subview.
If the cell is expanded than deque a expandedCell where the views are layed out as in the first screenshot.
After expanding and or collapsing call tableview.reloadData()
It used to be a long long time ago that UIViews clipped their children, but that hasn't been true for a very long time. If you want clipping on you need to either change UIView.clipsToBounds to true or use the underlying CALayer property maskToBounds.
cell.contentView.clipsToBounds = true
Or you can check the box in the storyboard/nib.

Custom UITableView cell image transform not applied to reusable cells

I have a custom UITableViewCell that displays a circular image on the left-hand side. Since the default UIImageView supplied with the UITableViewCell is the same height as the row, the images end up nearly touching. I'd like to shrink the image slightly to create some extra padding.
I was able to get this to work using the following code
override func layoutSubviews() {
super.layoutSubviews()
// Make the image view slightly smaller than the row height
self.imageView!.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
// Round corners
self.imageView!.layer.cornerRadius = self.imageView!.bounds.height / 2.0
self.imageView!.layer.borderWidth = 0.5
self.imageView!.layer.borderColor = UIColor.gray.cgColor
self.imageView!.layer.masksToBounds = true
self.imageView!.contentMode = .scaleAspectFill;
self.updateConstraints()
}
override func prepareForReuse() {
super.prepareForReuse()
self.imageView!.image = nil
self.layoutSubviews()
}
This works only for the cells displayed in the table view when it first appears on the screen. Once I scroll (i.e. dequeue a re-usable cell), the transform is no longer applied. The image below shows the left side of the table view. I've captured the region where the original cells transition to re-used cells.
For completeness, here is my tableView(cellForRowAt:) function
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.itemTableView.dequeueReusableCell(withIdentifier: "ItemCell") as! InventoryItemTableViewCell
if let items = self.displayedItems {
if indexPath.row < items.count {
let item = items[indexPath.row]
cell.item = item
cell.textLabel!.text = items[indexPath.row].partNumber
cell.detailTextLabel!.text = items[indexPath.row].description
if let quantity = items[indexPath.row].quantity {
cell.quantityLabel.text = "Qty: \(Int(quantity))"
}
else {
cell.quantityLabel.text = "Qty: N/A"
}
if let stringImageBase64 = item.imageBase64 {
let dataDecoded: Data = Data(base64Encoded: stringImageBase64, options: .ignoreUnknownCharacters)!
cell.imageView!.image = UIImage(data: dataDecoded)
}
else {
cell.imageView!.image = blankImage
}
}
}
return cell
}
I tried other methods such as playing with the image view's insets but this had no effect.
Question
Why is the transform being applied to the original cells when the table is created but not to any re-used cells? Should I be approaching this differently?
I was able to accomplish resizing the image using the following code. It seems the autolayout is applied after the transform, therefore overriding it.
override func layoutSubviews() {
// Layout all subviews except for the image view
super.layoutSubviews()
// Make the image view slightly smaller than the row height
self.imageView!.frame = CGRect(x: self.imageView!.frame.origin.x + 4,
y: self.imageView!.frame.origin.y + 4,
width: 56,
height: 56)
// Round corners
self.imageView!.layer.cornerRadius = self.imageView!.frame.height / 2.0
self.imageView!.layer.borderWidth = 0.5
self.imageView!.layer.borderColor = UIColor.gray.cgColor
self.imageView!.layer.masksToBounds = false
self.imageView!.clipsToBounds = true
self.imageView!.contentMode = .scaleAspectFit;
}

add subview to collectionview cell jsqmessagesviewcontroller

I'm trying to add a subview to each cell (message) of my collectionView (JSQMessagesViewController) to display time of my message, something like this:
Here is my code:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
let message = messages[indexPath.item]
let timeLabel = UILabel()
timeLabel.frame = cell.textView.frame
timeLabel.text = "abc"
timeLabel.textColor = .blue
cell.addSubview(timeLabel)
if message.senderId == senderId { // 1
cell.textView?.textColor = UIColor.black // 3
cell.avatarImageView.image = self.avatars.0.image
cell.avatarImageView.layer.cornerRadius = cell.avatarImageView.frame.size.height / 2
cell.avatarImageView.clipsToBounds = true
} else {
cell.textView?.textColor = UIColor.black // 2
cell.avatarImageView.image = self.avatars.1.image
cell.avatarImageView.layer.cornerRadius = cell.avatarImageView.frame.size.height / 2
cell.avatarImageView.clipsToBounds = true
}
return cell
}
But it adds me 2 labels:
Why there are 2 labels? And how can I add this label particularly to the bottom-right of my message? Thanks in advance!
Check JSQMessagesCollectionViewCellIncoming.nib and JSQMessagesCollectionViewCellIncoming.nib and adjust the Cell bottom label as per your need to make it look like your design.Adjust Autolayout constraint and done.
Problem 1
Basically, you are creating every time new instance of label.
let timeLabel = UILabel()
timeLabel.frame = cell.textView.frame
timeLabel.text = "abc"
timeLabel.textColor = .blue
Due to the concept of reuses, the cell will reuse everything for the next time. So when you add the subview of timeLabel for the first time that is ready to reuse for the next time. and you are adding again it let timeLabel = UILabel() while the label already there and you are putting a new instance every time.
Solution 1
You have to add the subview once and reuse it by using the tag.
Declare the let timeLabel :UILabel? at class level means where your all variables are declare and check its reference in the cellForItemAt atIndexPath like
if timeLabel == nil {
timeLabel = UILabel()
timeLabel.frame = cell.textView.frame
timeLabel.text = "abc"
timeLabel.textColor = .blue
timeLabel.tag = 766
cell.addSubview(timeLabel)
}
And last get it with the tag in the cellForItemAt atIndexPath
Problem 2
That is not in the bottom right because after awakeFromNib() in JSQMessagesCollectionViewCell this is not adding means label adds before setupLayout.
Solution 2
A: You have to add the constraint manually.
B: OR you can try by setting the frame at last line before returning cell.

Varying length cells in TableView make the view snap while scrolling Swift

So I have a TableView with 2 prototype cells. Here is my code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! ItemDetailsCell
cell.centerImage.image = mainImage
cell.heading.text = detailsHeadings[indexPath.row]
let headingString = detailsHeadings[indexPath.row]
cell.body.text = details[headingString]
tableView.rowHeight = cell.labelBlack.frame.height + 40
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell2", forIndexPath: indexPath) as! ItemDetailsCell
cell.heading.text = detailsHeadings[indexPath.row]
let headingString = detailsHeadings[indexPath.row]
cell.body.text = details[headingString]
let bodyString = details[headingString]
let labelWidth = Int(cell.body.frame.width)
println(labelWidth)
let label = UILabel(frame: CGRect(x: 0, y: 0, width: labelWidth, height: 10000))
label.text = bodyString
label.numberOfLines = 100
label.font = UIFont(name: "OpenSans-Light", size: 12.0)
label.sizeToFit()
tableView.rowHeight = label.frame.height + 3
return cell
}
}
So the second prototype cell has just two labels with the values being assigned from a Dictionary. The cell size needs to expand or contract based upon how many lines of text there are. In auto layout I have the number of lines set to 0 so it will pick however many lines are needed. This works fine except when you scroll within the app it will snap the view up as users scroll back up from the bottom. Is there a way to avoid this?
Thanks in advance for any help.
I found this actually after spending some more time looking:
http://candycode.io/automatically-resizing-uitableviewcells-with-dynamic-text-height-using-auto-layout/
It gave me what I needed. I removed the parts of my code that set the rowHeight and then used the viewDidLoad method as well as auto layout to constrain my cell sizes and after a bit of trial and error it is working without snapping to place when you scroll.
You are changing the rowHeight of the tableview. That's most likely very wrong. The rowHeight of the tableView is the default for all rows that don't have their own height, so you are effectively changing the height of all cells.
You should use the delegate method tableView:heightForRowAtIndexPath:

Images in UIImageView not showing in UITableView when circular mask is applied to the ImageView unless scroll

Thanks in advance for the help.
I have a UITableView within a main view contoller. Within the prototype cell, I have a UIImageview. In the code below everything works until I add the 5 lines to apply a circular mask and border. Once I do that, the images will not load unless I scroll the cells. The mask and border do get applied perfectly however. Will be great when it works... but until then.
Certainly this has been seen before. I'm a swift/objective-C newbie.
Working in swift for this one.
Code below;
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("mixerCell", forIndexPath: indexPath) as! MixerTableViewCell
// set label background color to clear
cell.textLabel?.backgroundColor = UIColor.clearColor()
// set highlight selection to none
cell.selectionStyle = .None
// set image for cell
let imageView = cell.viewWithTag(1) as! UIImageView
// put circular mask and border. This is the problem code that causes initial load of the tableview images to show up blank.
imageView.layer.cornerRadius = imageView.frame.size.width / 2;
imageView.clipsToBounds = true;
let color1 = UIColor(white: 1.0, alpha: 0.5).CGColor as CGColorRef
imageView.layer.borderWidth = 2;
imageView.layer.borderColor = color1
// assign image
imageView.image = UIImage(named: mixerSounds[indexPath.row])
return cell
}
initial view load
after scroll
your code is perfectly working for me. Here i am using Xcode-7. i think you are using Xcode-6.3 or less version. just upgrade it to Xcode- 7. and if you are using the same then just check your heightforRowAtIndexpath or other delegates there should be some issue.
thanks
Try changing the below lines,
// replace this
let imageView = cell.viewWithTag(1) as! UIImageView
// to
let imageView = cell.yourImageViewName
/* yourImageViewName is the outlet
reference name you have given in the
MixerTableViewCell custom class.
*/
Edit 2: Just for debugging purposes,
hardcode the image name and check if the image appears on the all the cells.
imageView.image = UIImage(named: "first1.png")
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cellIdentifier = "cell"
var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell
cell.image_View.image = UIImage(named: mixerSounds[indexPath.row])
println("The loaded image: \(image)")
cell.image_View.layer.masksToBounds = false
cell.image_View.layer.borderColor = UIColor.blackColor().CGColor
cell.image_View.layer.cornerRadius = image.frame.height/2
cell.image_View.clipsToBounds = true
return cell
}
Give imageview outlet to cell and not give imageview name because by default name is imageview so take diffrent name
It looks like the problem is using clipToBounds = true I am facing the same issue while making circular UIImageView inside UITableViewCell
I didn't find the exact solution but for now I found a way to do this
if (indexPath.row == indexPath.length && !isTableReloaded)
{
let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.000000001 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.reloadTableView()
})
}
func reloadTableView()
{
isTableReloaded = true
self.tableViewContacts.reloadData()
}
Here isTableReloaded is a Bool type var which is initialized to false in viewDidLoad()
and the if condition is to be placed at the last of cellForRowAtIndexPath but before return statement
This will resolve our problem but do not rely on this as this is not the best approach.
Please post solution for this if any one found the better approach.
Here is a perfect and state away solution for circular image in UITableview Cell.
Simply modify your UITableviewCell (custom cell) class with below code.
override func awakeFromNib() {
super.awakeFromNib()
imgEvent.layer.frame = (imgEvent.layer.frame).insetBy(dx: 0, dy: 0)
imgEvent.layer.borderColor = UIColor.gray.cgColor
imgEvent.layer.cornerRadius = (imgEvent.frame.height)/2
imgEvent.layer.masksToBounds = false
imgEvent.clipsToBounds = true
imgEvent.layer.borderWidth = 0.5
imgEvent.contentMode = UIViewContentMode.scaleAspectFill
}
It will also helps to solve the problem of image circular only after scrolling table..(if any!)
let width = cell.frame.size.width
cell.img.layer.cornerRadius = width * 0.72 / 2
0.72 is the ratio of the cell width to image width, for eg. cellWidth = 125 and imageWidth = 90, so 125/90 would give 0.72. Do similar calculation for your image.
First: Images doesn't load until you scroll, because when cellForRowAtIndexPath methods called the constraints doesn't set for image until now, so when scrolling the constraints was added and the image appears, so if you set proportional width and height for imageView (width==height) in cell then
do that
let w = tableview.frame.width*(proportional value like 0.2)
imageView.layer.cornerRadius = w / 2
imageView.clipsToBounds = true;

Resources