Trying to set a button with an image in tableView:editActionsForRowAt: - ios

Working with Xcode 9.2, in a UITableView, I am setting a button with an image and no text when a cell is left swiped.
Here is the precise code I am using:
func tableView(_ tableView: UITableView,
editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let myButton = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("myButton was tapped")
}
myButton.backgroundColor = UIColor(patternImage: UIImage(named: "myImage")!)
return [myButton]
}
It works but the image appears at the bottom of the cell.
How can I bring it to the center?
Just in case, here is the image I am using for testing (70x70 pixels):
And here is how it looks in the table view:

This is one of the way. You have to create image with UIGraphicsBeginImageContextWithOptions.
func swipeCellButtons() -> UIImage
{
let commonWid : CGFloat = 40
let commonHei : CGFloat = 70 // EACH ROW HEIGHT
var img: UIImage = UIImage(named: "pause") // TRY IMAGE COLOR WHITE
UIGraphicsBeginImageContextWithOptions(CGSize(width: commonWid, height: commonHei), false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(UIColor.clear.cgColor)
context!.fill(CGRect(x: 0, y: 0, width: commonWid, height: commonHei))
var img: UIImage = UIImage(named: "pause")!
img.draw(in: CGRect(x: 5, y: 15, width: 30, height: 30))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
func tableView(_ tableView: UITableView,
editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let myButton = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("myButton was tapped")
}
let patternImg = swipeCellButtons()
myButton.backgroundColor = UIColor(patternImage: patternImg)
return [myButton]
}

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

How to delete UITableViewCell with swipe-to-dismiss with fade effect and no red delete button?

After looking into a myriad of StackOverflow posts, nothing really answers how to delete a UITableViewCell with swipe-to-dismiss while fading and without the red delete button.
My Tableviewcell looks like a card, so the red frame of the delete button breaks the sense of continuity and elevation of these cells with card shapes.
Here is the code I am currently using to delete, which does not fade despite the .fade on the UITableViewRowAnimation.
func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
self.pastOrders.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
Here's a screenshot of the behavior I am trying to achieve:
Output 3
//TO CHANGE "DELETE" TITLE COLOR
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let toDelete = UITableViewRowAction(style: .normal, title: "") { (action, indexPath) in
print("\n\n Delete item at indexPathDelete item at indexPath")
}
let deleteTextImg = swipeCellButtons(labelText: "Delete", textColor: UIColor.darkGray, alphaVal: 1.0)
toDelete.backgroundColor = UIColor(patternImage: deleteTextImg)
return [toDelete]
}
func swipeCellButtons(labelText : String, textColor: UIColor, alphaVal: CGFloat) -> UIImage
{
let commonWid : CGFloat = 40
let commonHei : CGFloat = 70 // ROW HEIGHT
let label = UILabel(frame: CGRect(x: 0, y: 0, width: commonWid, height: commonHei))
label.text = labelText
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 11)
label.textColor = textColor.withAlphaComponent(alphaVal)
UIGraphicsBeginImageContextWithOptions(CGSize(width: self.view.frame.width, height: commonHei), false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(UIColor.clear.cgColor) // YOU CAN GIVE YOUR BGCOLOR FOR DELETE BUTTON
context!.fill(CGRect(x: 0, y: 0, width: (self.view.frame.width) / 3, height: commonHei))
label.layer.render(in: context!)
//If you want to add image instead of text, uncomment below lines.
//Then, comment this "label.layer.render(in: context!)" line
//var img: UIImage = UIImage(named: "deleteIcon")!
//img.draw(in: CGRect(x: 0, y: 0, width: 30, height: 30))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
Output 2:
// INSIDE CELL FOR ROW AT INDEXPATH
// COMMENT THIS LINE
//cell.addGestureRecognizer(swipeGesture)
// CELL FADE WILL NOT WORK HERE
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let toDelete = UITableViewRowAction(style: .normal, title: " ") { (action, indexPath) in
print("\n\n Delete item at indexPathDelete item at indexPath")
}
toDelete.backgroundColor = .white
return [toDelete]
}
Output 1:
// GLOBAL DECLARATION
var gotCell : DefaultTableViewCell?
var alphaValue : CGFloat = 1.0
var deletingRowIndPath = IndexPath()
// INSIDE CELL FOR ROW AT INDEXPATH
//let cell = tableView.dequeueReusableCell(withIdentifier: "default", for: indexPath) as! DefaultTableViewCell
let cell = DefaultTableViewCell() // Add this line and comment above line. The issue is `dequeuingreusingcell`. In this method, it will stop dequeuing. But, we have to customise `UITableViewCell` in coding.
let swipeGesture = UIPanGestureRecognizer(target: self, action: #selector(handleSwipe))
swipeGesture.delegate = self
cell.addGestureRecognizer(swipeGesture)
func handleSwipe(panGesture: UIPanGestureRecognizer) {
if panGesture.state == UIGestureRecognizerState.began {
let cellPosition = panGesture.view?.convert(CGPoint.zero, to: defTblVw)
let indPath = defTblVw.indexPathForRow(at: cellPosition!)
deletingRowIndPath = indPath!
gotCell = defTblVw.cellForRow(at: indPath!) as! DefaultTableViewCell
}
if panGesture.state == UIGestureRecognizerState.changed
{
let isLeftMoving = panGesture.isLeft(theViewYouArePassing: (gotCell)!)
if isLeftMoving == true
{
self.gotCell?.alpha = self.alphaValue
self.gotCell?.frame.origin.x = (self.gotCell?.frame.origin.x)! - 2.5
self.view.layoutIfNeeded()
self.alphaValue = self.alphaValue - 0.005
}
else // ADD THIS ELSE CASE
{
self.alphaValue = 1.0
self.gotCell?.alpha = 1.0
UIView.animate(withDuration: 0.8, animations: {
self.gotCell?.frame.origin.x = 0
self.view.layoutIfNeeded()
}) { (value) in
}
}
}
if panGesture.state == UIGestureRecognizerState.ended
{
self.alphaValue = 1.0
if (self.gotCell?.frame.origin.x)! < CGFloat(-(defTblVw.frame.size.width - 90))
{
myArr.remove(at: (deletingRowIndPath.row))
defTblVw.beginUpdates()
defTblVw.deleteRows(at: [deletingRowIndPath], with: UITableViewRowAnimation.fade)
defTblVw.endUpdates()
}
else
{
UIView.animate(withDuration: 0.8, animations: {
self.gotCell?.alpha = 1.0
self.gotCell?.frame.origin.x = 0
self.view.layoutIfNeeded()
}) { (value) in
}
}
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
extension UIPanGestureRecognizer {
func isLeft(theViewYouArePassing: UIView) -> Bool {
let velocityVal : CGPoint = velocity(in: theViewYouArePassing)
if velocityVal.x >= 0 {
return false
}
else
{
print("Gesture went other")
return true
}
}
}
=============================
I guess SwipeCellKit pod is an option as well to do swiping without delete button, so please check out this link: https://github.com/SwipeCellKit/SwipeCellKit.
There is all documentation how you can customize it and if you can see the Destructive gif on the link, it is what you wanted, however you have to make it custom so there is no other buttons nor the delete button as well.
I hope it helped you somehow.
You can use this library with .exit mode
and In your cellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SwipyCell //User You cell identifier and class of cell here
let checkView = viewWithImageName("check") //Use some white dummy image
cell.addSwipeTrigger(forState: .state(0, .left), withMode: .exit, swipeView: checkView, swipeColor: tableView.backgroundView?.backgroundColor, completion: { cell, trigger, state, mode in
print("Did swipe \"Checkmark\" cell")
})
return cell
}
Hope this will help you
Try this code.
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let toDelete = UITableViewRowAction(style: .normal, title: " ") { (action, indexPath) in
self.rows.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
toDelete.backgroundColor = .white
return [toDelete]
}
Hope it would help you.
You can animate the content view and on completion of animation you can delete the cell
You can add a swipe gesture on the content of your custom cell, when the swipe animation is over you call a delegate method to the ViewController in which it will update the data array delete the tableView row and reload the tableView.

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
}
}

function inside tableView:cellForRowAtIndexPath: only called for the first 2 cell

So I have this function:
func addActivityIndicator(_ aspectRatio: CGFloat?, index: Int) {
if activityIndicator == nil {
print("add activity indicator")
print("at index: \(index)")
let screenWidth = UIScreen.main.bounds.width
let height: CGFloat
if let aspectRatio = aspectRatio {
height = screenWidth / aspectRatio
} else {
print("aspect ratio is nil")
height = screenWidth
}
let indicatorFrame = CGRect(x: (screenWidth - 60) / 2,
y: (height - 60) / 2,
width: 60,
height: 60)
self.activityIndicator = NVActivityIndicatorView(frame: indicatorFrame,
type: .ballClipRotateMultiple,
color: .lightGray,
padding: nil)
self.addSubview(self.activityIndicator!)
self.activityIndicator?.startAnimating()
}
}
which just basically add an activity indicator in the center of the superview.
and then I called this function inside cellForRow:AtIndexPath:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let contentCell = tableView.dequeueReusableCell(withIdentifier: "ContentCell", for: indexPath) as! ContentCell
contentCell.thumbnail.addActivityIndicator(content.aspectRatio, index: indexPath.row)
return contentCell
}
thumbnail is a uiimageview which will be added an activity indicator. But in the log I only see this:
add activity indicator at index: 0
add activity indicator at index: 1
add activity indicator at index: 2
How do I make the addActivityIndicator function works on all cell?
Add the ActivityIndicator on func awakeFromNib of ContentCell

How add custom image to uitableview cell swipe to delete

Could you tell me, how to add custom image to delete button when swipe cell on UITableview?
search you need function "editActionsForRowAtIndexPath", where you create scope of actions. You need to set UIImage to backgroundColor of UITableViewRowAction.
let someAction = UITableViewRowAction(style: .Default, title: "") { value in
println("button did tapped!")
}
someAction.backgroundColor = UIColor(patternImage: UIImage(named: "myImage")!)
There's this UITableView delegate function you can make use of:
#available(iOS 11.0, *)
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .normal, title: "", handler: {a,b,c in
// example of your delete function
self.YourArray.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
})
deleteAction.image = UIImage(named: "trash.png")
deleteAction.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [deleteAction])
}
PS: Personally, I think icon size 32 is the best
100 % working Swipable cell with custom image and size of image with background color ios swift #ios #swift #ios13 #ios14
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style: .normal, title: "", handler: { (action,view,completionHandler ) in
self.selectedIndex = indexPath.row
self.deleteNotification()
completionHandler(true)
})
if #available(iOS 13.0, *) {
action.image = UIGraphicsImageRenderer(size: CGSize(width: 30, height: 30)).image { _ in
UIImage(named: "delete-1")?.draw(in: CGRect(x: 0, y: 0, width: 30, height: 30))
}
action.backgroundColor = UIColor.init(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 0.0)
let confrigation = UISwipeActionsConfiguration(actions: [action])
return confrigation
} else {
// Fallback on earlier versions
let cgImageX = UIImage(named: "delete-1")?.cgImage
action.image = OriginalImageRender(cgImage: cgImageX!)
action.backgroundColor = UIColor.init(hex: "F7F7F7")
let confrigation = UISwipeActionsConfiguration(actions: [action])
return confrigation
}
}

Resources