Circular Image in UITableViewCell - ios

I'm trying to create circular Images inside of my TableViewCell but something is going wrong. I guess that the cornerRadius is to big because there is not any Image displayed with this Code. If I set the cornerRadius on 30 for example, I can see the Images rounded but not in a clear circle .Why is this not working ?
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "QuestionLine")
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "QuestionLine")
let user = users[indexPath.row]
cell?.textLabel?.text = user.question
cell?.detailTextLabel?.text = user.name
let image = UIImage(named: user.profilePicture)
cell?.imageView?.image = image
cell?.imageView?.layer.cornerRadius = (image?.size.width)! / 2
cell?.imageView?.clipsToBounds = true
return cell!
}

The UITableViewCell's imageView will be sized to the height of the cell, If you want to customize the size of imageView try the below code.
let image = UIImage(named: user.profilePicture)
cell?.imageView?.image = image
let itemSize = CGSize.init(width: 40, height: 40) // your custom size
UIGraphicsBeginImageContextWithOptions(itemSize, false, UIScreen.main.scale);
let imageRect = CGRect.init(origin: CGPoint.zero, size: itemSize)
cell?.imageView?.image!.draw(in: imageRect)
cell?.imageView?.image! = UIGraphicsGetImageFromCurrentImageContext()!;
UIGraphicsEndImageContext()
cell?.imageView?.layer.cornerRadius = (itemSize.width) / 2
cell?.imageView?.clipsToBounds = true

Try this code
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "QuestionLine")
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "QuestionLine")
let user = users[indexPath.row]
cell?.textLabel?.text = user.question
cell?.detailTextLabel?.text = user.name
let image = UIImage(named: user.profilePicture)
cell?.imageView?.image = image
cell?.imageView?.layer.cornerRadius = (cell?.imageView?.frame.size.width)! / 2
cell?.imageView?.layer.masksToBounds = true
cell?.imageView?.layer.borderColor = colour.cgColor
cell?.imageView?.layer.borderWidth = 1
return cell!
}

First make sure your image height and width is equal, then replace last 3 lines of code with these lines-
cell?.imageView?.layer.cornerRadius = (image?.frame.size.width)! / 2
cell?.imageView?.masksToBounds = true
return cell!

Related

How to set fix ImageView size inside TableViewCell programmatically?

I have this issue wherein I need to fix the size on an image inside a tableviewcell. The image below shows that the image size in not uniform.
Here are the codes I used.
if noteImageIsAvailable == true {
if let imageData = assignedNotePhoto?.photoData {
if let image = Utilities.resizePictureImage(UIImage(data: imageData as Data)) {
//added fix
cell.imageView?.frame.size = CGSize(width: 36, height: 24)
cell.imageView?.clipsToBounds = true
//-----
cell.imageView?.contentMode = .scaleAspectFill
cell.imageView?.image = image
}
}
}
I read an answer here in stackoverflow. It says I need to add clipToBounds but unfortunately it doesn't work. Please help me solve this issue. Thank you
TableView Code
extension SettingNoteViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Menu.SettingNote.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "reuseIdentifier")
let keyPass = KeyHelper.NoteSetting.info[indexPath.row]
let assignedNotePhoto = self.getNotePhoto(key: keyPass.key)
let assignedNoteTextData = self.getNoteTextData(key: keyPass.key)?.value
cell.contentView.backgroundColor = .black
cell.textLabel?.textColor = .white
cell.detailTextLabel?.textColor = .white
cell.detailTextLabel?.numberOfLines = 0
let noteImageIsAvailable = assignedNotePhoto?.photoData != nil
if noteImageIsAvailable == true {
if let imageData = assignedNotePhoto?.photoData {
if let image = Utilities.resizePictureImage(UIImage(data: imageData as Data)) {
//added fix
cell.imageView?.translatesAutoresizingMaskIntoConstraints = false
cell.imageView?.frame.size = CGSize(width: 36, height: 24)
cell.imageView?.clipsToBounds = true
//-----
cell.imageView?.contentMode = UIView.ContentMode.scaleAspectFit
cell.imageView?.image = image
}
}
}
cell.textLabel?.text = Menu.SettingNote.items[indexPath.row].value
cell.detailTextLabel?.text = assignedNoteTextData ?? "noteSettingSubTitle".localized
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is InputMemoViewController {
let vc = segue.destination as! InputMemoViewController
vc.infoNoteKey = self.infoNoteKeyToPass
vc.infoLabelText = self.infoLabelToPass
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.infoNoteKeyToPass = KeyHelper.NoteSetting.info[indexPath.row].key
self.infoLabelToPass = KeyHelper.NoteSetting.info[indexPath.row].label
self.performSegue(withIdentifier: "showInputMemo", sender: self)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
}
The image below was the output when I applied #Kishan Bhatiya solution.
The first and second image is a landscape photo and the third image is in portrait
When you add cell.imageView?.translatesAutoresizingMaskIntoConstraints = false then frame has no effect, so try to use any one
let marginguide = contentView.layoutMarginsGuide
//imageView auto layout constraints
cell.imageView?.translatesAutoresizingMaskIntoConstraints = false
let marginguide = cell.contentView.layoutMarginsGuide
cell.imageView?.topAnchor.constraint(equalTo: marginguide.topAnchor).isActive = true
cell.imageView?.leadingAnchor.constraint(equalTo: marginguide.leadingAnchor).isActive = true
cell.imageView?.heightAnchor.constraint(equalToConstant: 40).isActive = true
cell.imageView?.widthAnchor.constraint(equalToConstant: 40).isActive = true
cell.imageView?.contentMode = .scaleAspectFill
cell.imageView?.layer.cornerRadius = 20 //half of your width or height
And it's better to set constraints in UITableViewCell
you know your imageview size. So you can resize your UIImage to imageView size.
extension UIImage{
func resizeImageWithHeight(newW: CGFloat, newH: CGFloat) -> UIImage? {
UIGraphicsBeginImageContext(CGSize(width: newW, height: newH))
self.draw(in: CGRect(x: 0, y: 0, width: newW, height: newH))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
use like that
let img = originalImage.resizeImageWithHeight(newW: 40, newH: 40)
cell.imageView?.image = img
Here, 40 40 is my imageview size
I have the same problem, scaleAspectFit is solved for me. you can try it
cell.imageView.contentMode = UIViewContentMode.scaleAspectFit

Stack spacing is ignored

I've an image which is downloaded async. This image is resized to fit the screen by a constraint. This seems to break the vertical spacing between the image and label in the table cell which is set by the stack view.
Table view
xCode constrains
Result iPhone 5S
Code to set the image height:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "overview", for: indexPath) as! OverviewCell
let upload = images[indexPath.row]
cell.titleLabel.text = upload.image.title
let cachedImage = cachedImages[upload.image.imageUrl]
if let cachedImage = cachedImage {
var cellFrame = cell.frame.size
cellFrame.width = cellFrame.width - 30
let resizedImage = cachedImage.resize(width: cellFrame.width)
cell.imageView!.image = resizedImage
cell.heightConstrain.constant = resizedImage.size.height
}
else {
DispatchQueue.global().async {
let data = try! Data.init(contentsOf: URL.init(string: upload.image.imageUrl)!)
DispatchQueue.main.sync {
self.cachedImages[upload.image.imageUrl] = UIImage.init(data:data)!
self.tableView.beginUpdates()
self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.fade)
self.tableView.endUpdates()
}
}
}

Swift - How to change the color of an accessoryType (disclosureIndicator)?

I have question about the accessoryType of cells. I am using a cell with an disclosureIndicator as accessoryType and I want to change it's color but I can't.
Does anyone know if this is a bug or if Apple forces me to use the grey color?
Actually I can change the colors of other accessoryType.
My code looks like this:
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! customCell
cell.tintColor = UIColor.red
cell.accessoryType = .disclosureIndicator
And my arrow is still grey. But if I use a checkmark accessoryType it becomes red.
Is there any way to fix this or do I have to use a colored image?
You can do something like this
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.tintColor = UIColor.white
let image = UIImage(named: "Arrow.png")
let checkmark = UIImageView(frame:CGRect(x:0, y:0, width:(image?.size.width)!, height:(image?.size.height)!));
checkmark.image = image
cell.accessoryView = checkmark
let object = objects[indexPath.row] as! NSDate
cell.textLabel!.text = object.description
return cell
}
Sample Arrow Images
Output
Use SF Symbol
let image = UIImage(systemName: "chevron.right")
let accessory = UIImageView(frame:CGRect(x:0, y:0, width:(image?.size.width)!, height:(image?.size.height)!))
accessory.image = image
// set the color here
accessory.tintColor = UIColor.white
cell.accessoryView = accessory
Updated for Swift 4.2 with images attached:
cell.accessoryType = .disclosureIndicator
cell.tintColor = .black
let image = UIImage(named:"disclosureArrow")?.withRenderingMode(.alwaysTemplate)
if let width = image?.size.width, let height = image?.size.height {
let disclosureImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
disclosureImageView.image = image
cell.accessoryView = disclosureImageView
}
Images you can use:
What it could look like:
Bellow Code is Swift 3.0 code, and will change the accessoryType color as per tintColor.
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "SOME TITLE GOES HERE"
cell.accessoryType = .disclosureIndicator
cell.tintColor = UIColor.blue
let image = UIImage(named:"arrow1")?.withRenderingMode(.alwaysTemplate)
let checkmark = UIImageView(frame:CGRect(x:0, y:0, width:(image?.size.width)!, height:(image?.size.height)!));
checkmark.image = image
cell.accessoryView = checkmark
return cell
}
Swift 5. Extension style ;)
extension UITableViewCell {
func setupDisclosureIndicator() {
accessoryType = .disclosureIndicator
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 7, height: 12))
imgView.contentMode = .scaleAspectFit
imgView.image = UIImage(named: "your_icon_name")
accessoryView = imgView
}
}
Swift 5 & iOS 15 & Xcode 13
Here is an extension which uses SF Symbols, so you have a chevron like the default disclosure indicator one:
extension UITableViewCell {
func addCustomDisclosureIndicator(with color: UIColor) {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 10, height: 15))
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 15, weight: .regular, scale: .large)
let symbolImage = UIImage(systemName: "chevron.right",
withConfiguration: symbolConfig)
button.setImage(symbolImage?.withTintColor(color, renderingMode: .alwaysOriginal), for: .normal)
button.tintColor = color
self.accessoryView = button
}
}
You can use it like this:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.addCustomDisclosureIndicator(with: .white) // Here your own color
return cell
}
Swift 5 & iOS 11-15
A combination of some answers
extension UITableViewCell {
func addCustomDisclosureIndicator(with color: UIColor) {
accessoryType = .disclosureIndicator
let disclosureImage = UIImage(named: "arrow_right")?.withRenderingMode(.alwaysTemplate)
let imageWidth = (disclosureImage?.size.width) ?? 7
let imageHeight = (disclosureImage?.size.height) ?? 12
let accessoryImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: imageWidth, height: imageHeight))
accessoryImageView.contentMode = .scaleAspectFit
accessoryImageView.image = disclosureImage
accessoryImageView.tintColor = color
accessoryView = accessoryImageView
}
}

UITableCell Swipe/Slide Two or Multiple Cell

I want to ask how could I have done this feature you can see in this image:
I know the editActionsForRowAt but I think it's not the solution for the things I need to do, so I added UISwipeGestureRecognizer and my code look like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! ConnectionTableCell
let users = self.users[indexPath.section]
let user = users.userInfo[indexPath.row]
cell.userImage.image = UIImage(named: user.imageUrl)
cell.name.text = user.name
cell.work.text = user.work
cell.selectionStyle = .none
cell.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(didSwipeForRow)))
return cell
}
func didSwipeForRow(recognizer: UIGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.ended {
let swipeLocation = recognizer.location(in: self.tableView)
if let swipedIndexPath = tableView.indexPathForRow(at: swipeLocation) {
let textConstant = tableView.frame.width + 10
let imageConstant = tableView.frame.width - 20
let swipeView = UIView()
swipeView.backgroundColor = UIColor.themeColor(color: .green)
let cell = tableView.cellForRow(at: swipedIndexPath) as! ConnectionTableCell
let height = cell.frame.height
let users = self.users[swipedIndexPath.section]
let user = users.userInfo[swipedIndexPath.row]
cell.addSubview(swipeView)
swipeView.frame = CGRect(x: 0, y: 0, width: imageConstant - 60, height: height)
cell.name.leftAnchor.constraint(equalTo: self.tableView.rightAnchor, constant: textConstant).isActive = true
cell.work.leftAnchor.constraint(equalTo: self.tableView.rightAnchor, constant: textConstant).isActive = true
cell.userImage.rightAnchor.constraint(equalTo: self.tableView.rightAnchor, constant: imageConstant).isActive = true
}
}
}
But the problem is when I swipe a cell its also affecting the reused cell as you can see in this image :
Any suggestions on how to solve this issue? Thanks in advance guys.

UICollectionview view in cell incorrect drawing

I am writing a chat application for Apple-tv, and I have a problem with displaying custom view in cells chat.
I have code:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) ->
UICollectionViewCell {
var myCell = self.chatCollectionView.dequeueReusableCellWithReuseIdentifier(chatCellIdentifier, forIndexPath: indexPath) as UICollectionViewCell
myCell.removeFromSuperview()
currentIndexPath = indexPath
if flag[indexPath.row]==0
{
myCell = sendBotMessage(historyString[indexPath.row], path: indexPath, collectionView: chatCollectionView)
}
else
{
myCell = sendUserMessage(historyString[indexPath.row], path: indexPath, collectionView: chatCollectionView)
}
return myCell
}
Code displays two versions of messages, as can be seen, sendBotMessage and sendUserMessage. The problem is that the display of the next cell is rendered incorrectly previous. First run with 1 sendBotMessage-view
and after adding second item
second (right) messages adding by sendUserMessage, another view.
the whole history of "correspondence" is stored in historyString - an array of type string. Storage is organized correctly , as when scrolling display errors disappear. For this reason, it can be concluded about the correctness of the rendering functions .
All new message appends in historyString, after that calling reloadData() and collectionView must be redrawing.
Where I could make a mistake? I tried to clean the subview for collectionView, disable scrolling animation, it did not help.
Code of procedure drawing view's :
func sendUserMessage(text : String, path : NSIndexPath, collectionView : UICollectionView) -> UICollectionViewCell {
let userMessage = text
let textSize = getTextFrameSize(userMessage)
let frameForMessage = getFrameforView(textSize)
let messageBuble = Chat_ViewInCell(frame: frameForMessage, textSize: textSize, text: userMessage)
messageBuble.backgroundColor = UIColor.clearColor()
let avatarImage = UIImage(named: "\(userAvatar+1).png")
let avatarView = UIImageView(image: avatarImage)
avatarView.contentMode = UIViewContentMode.ScaleAspectFit
let totalX = messageBuble.frame.width + 340
let constraint = 1750 - totalX
messageBuble.frame.origin = CGPointMake(constraint, 0)
avatarView.frame = CGRectMake(messageBuble.frame.maxX + 10, 0, 170, 170)
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatCellIdentifier, forIndexPath: path) as! chatCollectionViewCell
//добавляем, счастье, радуемся
cell.addSubview(messageBuble)
cell.addSubview(avatarView)
return cell
}
func sendBotMessage(text : String, path : NSIndexPath, collectionView : UICollectionView) -> UICollectionViewCell {
let userMessage = text
let textSize = getTextFrameSize(userMessage)
let frameForMessage = getFrameforView(textSize)
let messageBuble = bot_ViewInCell(frame: frameForMessage, textSize: textSize, text: userMessage)
messageBuble.backgroundColor = UIColor.clearColor()
let avatarImage = UIImage(named: partner_image)
let avatarView = UIImageView(image: avatarImage)
avatarView.contentMode = UIViewContentMode.ScaleAspectFit
avatarView.frame = CGRectMake(0, 0, 170, 170)
let constraint = avatarView.frame.maxX + 10
messageBuble.frame.origin = CGPointMake(constraint, 0)
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatCellIdentifier, forIndexPath: path) as! chatCollectionViewCell
//добавляем в возвращаемую ячейку
cell.addSubview(avatarView)
cell.addSubview(messageBuble)
return cell
}
After recommended changes with custom CollectionCellView i have this code:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) ->
UICollectionViewCell {
var myCell = chatCollectionView.dequeueReusableCellWithReuseIdentifier(chatCellIdentifier, forIndexPath: indexPath) as! chatCollectionViewCell
var customCell = myCell
if flag[indexPath.row]==0
{
customCell = sendBotMessage(historyString[indexPath.row], path: indexPath, collectionView: collectionView)
}
else
{
customCell = sendUserMessage(historyString[indexPath.row], path: indexPath, collectionView: collectionView)
}
myCell = customCell
currentIndexPath = indexPath
return myCell
}
Function on drawing too have changes to custom cell:
func sendUserMessage(text : String, path : NSIndexPath, collectionView : UICollectionView) -> chatCollectionViewCell {
let userMessage = text
let textSize = getTextFrameSize(userMessage) //высчитали необходимые размеры лейбла
let frameForMessage = getFrameforView(textSize) //высчиитали необходимые размеры всей вьюшки
let messageBuble = Chat_ViewInCell(frame: frameForMessage, textSize: textSize, text: userMessage)
messageBuble.backgroundColor = UIColor.clearColor()
//создаем аватар
let avatarImage = UIImage(named: "\(userAvatar+1).png")
let avatarView = UIImageView(image: avatarImage)
avatarView.contentMode = UIViewContentMode.ScaleAspectFit
//считаем отступ до правого края, позицию аватара
let totalX = messageBuble.frame.width + 340
let constraint = 1750 - totalX
messageBuble.frame.origin = CGPointMake(constraint, 0)
avatarView.frame = CGRectMake(messageBuble.frame.maxX + 10, 0, 170, 170)
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatCellIdentifier, forIndexPath: path) as! chatCollectionViewCell
//добавляем, счастье, радуемся
cell.addSubview(messageBuble)
cell.addSubview(avatarView)
return cell
}
func sendBotMessage(text : String, path : NSIndexPath, collectionView : UICollectionView) -> chatCollectionViewCell {
let userMessage = text
let textSize = getTextFrameSize(userMessage) //высчитали необходимые размеры лейбла
let frameForMessage = getFrameforView(textSize) //высчиитали необходимые размеры всей вьюшки
//создали всю вьюшку
let messageBuble = bot_ViewInCell(frame: frameForMessage, textSize: textSize, text: userMessage)
messageBuble.backgroundColor = UIColor.clearColor()
//создаем аватар и считаем его позицию
let avatarImage = UIImage(named: partner_image)
let avatarView = UIImageView(image: avatarImage)
avatarView.contentMode = UIViewContentMode.ScaleAspectFit
avatarView.frame = CGRectMake(0, 0, 170, 170)
let constraint = avatarView.frame.maxX + 10
messageBuble.frame.origin = CGPointMake(constraint, 0)
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatCellIdentifier, forIndexPath: path) as! chatCollectionViewCell
//добавляем в возвращаемую ячейку
cell.addSubview(avatarView)
cell.addSubview(messageBuble)
return cell
}
But problem no fixes... What i makes wrong with custom CollectionViewCell? I must to do smth another?
The problem is fixed! I don't know about prepareForReuse() method. So simple, in custom cell class defeniton i must override it method, and call for subviews clearsContextBeforeDrawing and removeFromSuperview. Code here:
class chatCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var view: Chat_ViewInCell!
override func prepareForReuse() {
super.prepareForReuse()
for currView in self.subviews
{
currView.clearsContextBeforeDrawing = true
currView.removeFromSuperview()
}
}
All worked, without incorrect redrawing.

Resources