Working with images in tableView:editActionsForRowAt: - ios

Here is some trouble I am having working with a UITableView, more precisely using an image in tableView:editActionsForRowAt:
Below follows my relevant code. It may be useful to say that the images I am using here are all square 70x70.
func tableView(_ tableView: UITableView,
editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
var patternImg:UIImage?
let upBtn = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("upBtn button tapped")
}
patternImg = self.swipeCellImage(named: "UpIcn", side: 46.0)
upBtn.backgroundColor = UIColor(patternImage: patternImg!)
let downBtn = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("downBtn button tapped")
}
patternImg = swipeCellImage(named: "DownIcn", side: 46.0)
downBtn.backgroundColor = UIColor(patternImage: patternImg!)
let rmvBtn = UITableViewRowAction(style: .destructive, title: "") {
action, index in
print("rmvBtn button tapped")
}
patternImg = swipeCellImage(named: "TrashIcn", side: 46.0)
rmvBtn.backgroundColor = UIColor(patternImage: patternImg!)
return [downBtn,upBtn,rmvBtn]
}
func swipeCellImage(named name: String, side: CGFloat) -> UIImage? {
let theImage = UIImage(named: name)
UIGraphicsBeginImageContextWithOptions(CGSize(width: side*2, height: side), false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(UIColor.clear.cgColor)
theImage?.draw(in: CGRect(x: 0, y: 0, width: side, height: side))
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
Things work, but unfortunately only up to a certain point.
Looking at this screen shot shows that there is an unwanted repetion of the images. Clearly I want each button(Icon) to only appear one time each.
Can anyone see how I need to fix my code to avoid this repetion?

To avoid that, you have to increase width size asper your requirement.
UIGraphicsBeginImageContextWithOptions(CGSize(width: self.view.frame.width, height: commonHei), false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(textColor.cgColor) // CHECK BY CHANGE BGCOLOR
context!.fill(CGRect(x: 0, y: 0, width: (self.view.frame.width) / 3, height: commonHei)) // INCREASE WIDTH SIZE
var img: UIImage = UIImage(named: "pause")!
img.draw(in: CGRect(x: 0, y: 0, width: 30, height: 30)) // Change position as per ur requirement.
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
If you having more doubt, then check on the 3D view.

Related

UITableViewCell - Set background as gradient to Delete (Swipeable Action) - Swift 4.2

I have customized my trailing swipe action for UITableViewCell. It has an image along with title and background color. It has been done like this :
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .normal, title: ActionTitle.delete) { (deleteAction, view, handler) in
self.deleteAction(indexPath: indexPath)
return handler(true)
}
deleteAction.image = Common.getImageAndTitleForTableRowAction(title: ActionTitle.delete, actionImage: #imageLiteral(resourceName: "delete"))
deleteAction.backgroundColor = Color.orangeColor
let editAction = UIContextualAction(style: .normal, title: ActionTitle.edit) { (editAction, view, handler) in
self.selectedIndexPath = indexPath
self.editLoanRecord()
return handler(true)
}
editAction.image = Common.getImageAndTitleForTableRowAction(title: ActionTitle.edit, actionImage: #imageLiteral(resourceName: "edit"))
editAction.backgroundColor = Color.blueColor
return UISwipeActionsConfiguration(actions: [deleteAction, editAction])
}
Now I need to set the backgroundColor to a gradient.
Checked lots of questions on stackoverflow but unable to do so. Any help will be appreciated.
You could try to create a color from a gradient image like so :
func linearGradientColor(from colors: [UIColor], locations: [CGFloat], size: CGSize) -> UIColor {
let image = UIGraphicsImageRenderer(bounds: CGRect(x: 0, y: 0, width: size.width, height: size.height)).image { context in
let cgColors = colors.map { $0.cgColor } as CFArray
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(
colorsSpace: colorSpace,
colors: cgColors,
locations: locations
)!
context.cgContext.drawLinearGradient(
gradient,
start: CGPoint(x: 0, y: 0),
end: CGPoint(x: size.width, y:0),
options:[]
)
}
return UIColor(patternImage: image)
}
...
deleteAction.backgroundColor = linearGradientColor(
from: [.red, .blue],
locations: [0, 1],
size: CGSize(width: 100, height: 44)
)
But this code has some limitations. You can not guess the size of the action view. So depending on your needs, you can either repeat the color, stretch it or use a large image. Using a third party lib is also a good option.
In many cases, Apple's default implementations will only take you so far and any further customization requires re-implementing the feature.
This seems like one of those cases, since the contextual action is not a view, so you can't modify it to add a gradient like you would with other views, and its properties are limited.
Your options are: implement your own swiping cell, use a third party library (like this one), or simply use a solid color.

Using buttons/images in tableView:editActionsForRowAt:

I am trying to use tableView:editActionsForRowAt:, having buttons with images instead of the usual texts.
But there are some challenges. Can some one help me?
First here is what I have already working, -- with some precious help from SOF --
and here is what I would like to get ideally:
The relevant code is below. The first case above is obtained when having xShift and xShiftInc both set to 0.0.
The second case is obtained by initializing xShift to 30.0 and xShiftInc to 20.0.
Graphically I get the result I want, but the problem is that the touch areas for the buttons have not moved as the image. In other words in the second case, if I touch the trash it prints "upBtn touched!".
To get "rmvBtn touched!" printed I need touch at the left of the trash.
Beside, I am not very happy with using 46.0 for the height of the cell, I would like a more generic parameter (rowHeigh equals -1 so can't be used). But this is a different issue.
func tableView(_ tableView: UITableView,
editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
var patternImg:UIImage?, xShift = CGFloat(30.0)//CGFloat(0.0)//
let xShiftInc = CGFloat(20.0)//CGFloat(0.0)//
let downBtn = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("downBtn touched!")
}
patternImg = swipeCellImage(named: "DownIcn", side: 46.0, horizOffSet: xShift)
downBtn.backgroundColor = UIColor(patternImage: patternImg!)
let upBtn = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("upBtn touched!")
}
xShift += xShiftInc
patternImg = self.swipeCellImage(named: "UpIcn", side: 46.0, horizOffSet: xShift)
upBtn.backgroundColor = UIColor(patternImage: patternImg!)
let rmvBtn = UITableViewRowAction(style: .destructive, title: "") {
action, index in
print("rmvBtn touched!")
}
xShift += xShiftInc
patternImg = swipeCellImage(named: "TrashIcn", side: 46.0, horizOffSet: xShift)
rmvBtn.backgroundColor = UIColor(patternImage: patternImg!)
return [downBtn,upBtn,rmvBtn]
}
func swipeCellImage(named name: String, side: CGFloat, horizOffSet: CGFloat) -> UIImage? {
let theImage = UIImage(named: name)
UIGraphicsBeginImageContextWithOptions(CGSize(width: UIScreen.main.bounds.width,
height: side), false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(UIColor.clear.cgColor)
theImage?.draw(in: CGRect(x: horizOffSet, y: 0, width: side, height: side))
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
Simple logic..!! Manually we dont give xshift and xshiftinc.
Case 1:
let downBtn = UITableViewRowAction(style: .normal, title: "") {
action, index in
print("downBtn touched!")
}
Here title's text is "" [empty string with no spaces]
So, tableview automatically takes width 30px for each one. If we add text, it will increase it width sizes automatically.
Case 2:
let downBtn = UITableViewRowAction(style: .normal, title: " ") {
action, index in
print("downBtn touched!")
}
Here title's text is "" [empty string with 1 space]
So, tableview automatically takes width 35px for this.
Hope you understand above thing.
Now, I modified your code.
func tableView(_ tableView: UITableView,
editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let cell = tableView.cellForRow(at: indexPath) as! myTableViewCell
print("cell_height ", cell.frame.size.height) // U MAY GET ROW HEIGHT HERE
var patternImg:UIImage?
let downBtn = UITableViewRowAction(style: .normal, title: " ") {
action, index in
print("downBtn touched!")
}
patternImg = swipeCellImage(named: "down", rowHeight: cell.frame.size.height)
downBtn.backgroundColor = UIColor(patternImage: patternImg!)
let upBtn = UITableViewRowAction(style: .normal, title: " ") {
action, index in
print("upBtn touched!")
}
patternImg = self.swipeCellImage(named: "up", rowHeight: cell.frame.size.height)
upBtn.backgroundColor = UIColor(patternImage: patternImg!)
let rmvBtn = UITableViewRowAction(style: .destructive, title: " ") {
action, index in
print("rmvBtn touched!")
}
let patternIm = swipeCellImage(named: "trash", rowHeight: cell.frame.size.height)
rmvBtn.backgroundColor = UIColor(patternImage: patternIm!)
return [downBtn,upBtn,rmvBtn]
}
func swipeCellImage(name: String, rowHeight: CGFloat) -> UIImage? {
let imgYposition : CGFloat = (rowHeight - 30) / 2
// NOTE: This 30px is image height. Image height is always should be less than Rowheight.
let theImage = UIImage(named: name)
UIGraphicsBeginImageContextWithOptions(CGSize(width: UIScreen.main.bounds.width,
height: rowHeight), false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(UIColor.yellow.cgColor)
context!.fill(CGRect(x: 0, y: 0, width: (self.view.frame.width) / 3, height: side))
theImage?.draw(in: CGRect(x: 5, y: imgYposition, width: 30, height: 30))
// In this sample, am taking rowHeight as 44px.
// Images should be 30 * 30 sizes.
// I gave two spaces in title. So total width is 40px.
// So,x = 5px and y = 7px.
let resultImage : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return resultImage
}
Output
If you want to give more gap between three buttons, just increase title's empty text and calculate width and keep your image center.

Delay with CollectionView Did Select

I'm getting a second long delay when selecting my collectionview cell. Here is my current code for collection view did select:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = PopUpCellViewController(nibName: "PopUpCellViewController", bundle: nil)
self.navigationController?.pushViewController(vc, animated: true)
print("called")
let cell = collectionView.cellForItem(at: indexPath) as! AnnotatedPhotoCell
sourceCell = cell
vc.picture = resizeImage(image: cell.imageView.image!, targetSize: CGSize(width: (view.bounds.width - 45),height: 0))
vc.comment = cell.commentLabel
var image = UIImage(named: "back_button_thick")
image = image?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
self.navigationController?.navigationBar.backIndicatorImage = image
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = image
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
}
func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / image.size.width
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
I am using a UIViewControllerAnimatedTransitioning. I am confident that there is no delay with my UIViewControllerAnimatedTransitioning. There seems to be an issue with my CollectionView Did select function. If switch my code in my didSelect function for a print statement, there is no longera delay.
I have two solutions:
1: PROBABLY NOT: I'm guessing that it's giving an annoying delay because it's taking a while for your app to run the code to get the nib file, and change all those things while it shouldn't.
2: PROBABLY: Perhaps running it on a real device will help. Xcode always has bugs, so don't worry. :)

In Swift, How to add Badges on UIImageView in TableViewCell in TableView?

I am developing a social networking site where I have a TableView with multiple cells(rows), in each cell I have a "comment" and "like" Imageview, so whenever update comes from the server I have to increment the number of "comment" and "like" through badges. So how to add badges on UIImageView in TableViewCell in Swift
You can add Badge on UIImageView this way
func drawImageView(mainImage: UIImage, withBadge badge: UIImage) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(mainImage.size, false, 0.0)
mainImage.drawInRect(CGRectMake(0, 0, mainImage.size.width, mainImage.size.height))
badge.drawInRect(CGRectMake(mainImage.size.width - badge.size.width, 0, badge.size.width, badge.size.height))
let resultImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
and then use this function
let mainImage = UIImage(named: "main_image_name_here")
let badgeImage = UIImage(named: "badge_icon_name_here")
let myBadgedImage: UIImage = drawImageView(mainImage!, withBadge: badgeImage!)
myImageView.image = myBadgedImage//set Image on ImageView
check out this MIBadgeButton-Swift
Good luck
RDC's answer in SWIFT 3
func drawImageView(mainImage: UIImage, withBadge badge: UIImage) -> UIImage {
UIGraphicsBeginImageContextWithOptions(mainImage.size, false, 0.0)
mainImage.draw(in: CGRect(x: 0, y: 0, width: mainImage.size.width, height: mainImage.size.height))
badge.draw(in: CGRect(x: mainImage.size.width - badge.size.width, y: 0, width: badge.size.width, height: badge.size.height))
let resultImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return resultImage
}

How to horizontally position the backIndicatorImage in UINavigationBar

I use this code to add the insets to the backIndicator image for the navigaiton bar. However this only works for vertical positioning of the image.
I can only move the image towards top or bottom, but not towards left or right.
Looks like left/right inset is not working. I am not sure what could be the issue.
UIEdgeInsets insets = UIEdgeInsetsMake(0, 20, 0, 0); //(20,0,0,0) works fine
UIImage *backArrowImage = [[UIImage imageNamed:#"Back"] imageWithAlignmentRectInsets:insets];
[[UINavigationBar appearance] setBackIndicatorImage:backArrowImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:backArrowImage];
I also tried this:
UIImage * backArrowImage =[[UIImage imageNamed:#"Back"]resizableImageWithCapInsets:UIEdgeInsetsMake(0, 20, 0, 0) resizingMode:UIImageResizingModeStretch];
If this is not possible, do I need to go back to adding custom back button ?
I've tried so many ways and finally this works for me, you can redraw your pic, do as following:
UIImage *arrow = [UIImage imageNamed:#"Back_Normal"];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(arrow.size.width+10, arrow.size.height), NO, 0); // move the pic by 10, change it to the num you want
[arrow drawAtPoint:CGPointMake(10, 0)];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
then use the finalImage as your backIndicatorImage.
Swift version. For Image only back btn. Maintains the swipe to go back built in gesture recognizers for iOS 7+.
func setBackBtnTarget(target: AnyObject?, action: Selector) {
var backImg: UIImage = // Your img
let leftPadding: CGFloat = 10
let adjustSizeForBetterHorizontalAlignment: CGSize = CGSizeMake(backImg.size.width + leftPadding, backImg.size.height)
UIGraphicsBeginImageContextWithOptions(adjustSizeForBetterHorizontalAlignment, false, 0)
backImg.drawAtPoint(CGPointMake(leftPadding, 0))
backImg = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
navigationController?.navigationBar.backIndicatorImage = backImg
navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImg
let backBtn: UIBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: target, action: action)
navigationItem.backBarButtonItem = backBtn
}
zhoujialei in a form of extension
extension UIImage {
func translatedHorizontally(by constant: CGFloat) -> UIImage? {
let newSize = CGSize(width: size.width + constant, height: size.height)
let newPoint = CGPoint(x: constant, y: 0.0)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
draw(at: newPoint)
let translatedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return translatedImage
}
}
Try adding a image with the positioning already inset so you don't need to do inset after. It's a hack but it should work
Swift 4.1 (Xcode 9.4.1)
If you don't know alignmentRectInsets, you can read this blog first.
For simplify the answer, we need some adjust for image edge insets.
extension UIImage {
func withInsets(_ insets: UIEdgeInsets) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(
CGSize(width: size.width + insets.left + insets.right,
height: size.height + insets.top + insets.bottom),
false,
self.scale)
let origin = CGPoint(x: insets.left, y: insets.top)
self.draw(at: origin)
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithInsets
}
}
https://stackoverflow.com/a/32576128/6113158
by Stefan Kendall
And here are the example for how to use it.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupNavBar(with: backImage)
}
func setupNavBar(with backIcon: UIImage) {
navBarLeftImageInsects = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 0)
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
if let backImage = backIcon.withInsets(navBarLeftImageInsects) {
navigationController?.navigationBar.backIndicatorImage = backImage
navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
}
}

Resources