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

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
}

Related

Change the size of UIImageView in a UITableViewCell

I am a newbie in Swift And Xcode.
Is there any clean way to change UIImageView Size placed inside a UITableViewCell?
This is my TableView, I want to align left label correctly
Replace
cell.imageView!.image = image
with
cell.imageView?.image = image.scaleImage(toSize: CGSize(width: 40, height: 40))
Add this UIImageView extension to the project.
extension UIImage {
func scaleImage(toSize newSize: CGSize) -> UIImage? {
var newImage: UIImage?
let newRect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height).integral
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
if let context = UIGraphicsGetCurrentContext(), let cgImage = self.cgImage {
context.interpolationQuality = .high
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: newSize.height)
context.concatenate(flipVertical)
context.draw(cgImage, in: newRect)
if let img = context.makeImage() {
newImage = UIImage(cgImage: img)
}
UIGraphicsEndImageContext()
}
return newImage
}
}
Reason for the error in your code is UITableViewCell is assigning the various size for UIImageView based on the image size inside it. In addition to I've adjusted the image to specific size so that it can fit to imageView content in UITableViewCell.
Note:
Please don't post the screenshots of the code as it does not help to others to copy it from the question and which attracts -ve voting as well. However, you can post the screenshots for XIBs, Storyboards and for Simulation errors.
You could use a stack view or a view with proportianal constraint to the relavite view in your cell xib. In this way the portion of the flag will be always the same.
Otherwise, if you want to do it quickly, you can contraint the leading of the label to the cell view and not with the flag. It's a litte bit dirtier but it should work.
Fix the width of the UIImageView and set its contentMode to .scaleAspectFit.
myImageView.safeAreaLayoutGuide.widthAnchor.constraint(equalToConstant: 50).isActive = true
myImageView.contentMode = .scaleAspectFit
myImageView.clipsToBounds = true
However, if you are using the storyboard, you can set these properties there itself.

Working with images in tableView:editActionsForRowAt:

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.

how to display view when orientation is changed is swift? [duplicate]

This question already has answers here:
How can I rearrange views when autorotating with autolayout?
(2 answers)
Closed 6 years ago.
I want to display one view when it is in portrait mode, and another view in landscape mode. Is it possible? I tried this code but view is overlapping. How to fix it?
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
let app = UIApplication.sharedApplication()
if (app.statusBarOrientation.isLandscape)
var imageView : UIImageView
imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 1300
, height: 50))
imageView.contentMode = UIViewContentMode.ScaleToFill
// UIGraphicsBeginImageContext(self.view.frame.size)
imageView.image = UIImage(named:"Header")
self.view.addSubview(imageView)
UIGraphicsBeginImageContext(self.view.frame.size)
UIImage(named: "Landscape")?.drawInRect(self.view.bounds)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.view.backgroundColor = UIColor(patternImage: image)
display1()
if (app.statusBarOrientation.isPortrait)
var imageView : UIImageView
imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 800
, height: 50))
imageView.contentMode = UIViewContentMode.ScaleToFill
// UIGraphicsBeginImageContext(self.view.frame.size)
imageView.image = UIImage(named:"Header")
self.view.addSubview(imageView)
UIGraphicsBeginImageContext(self.view.frame.size)
UIImage(named: "background")?.drawInRect(self.view.bounds)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.view.backgroundColor = UIColor(patternImage: image)
print(String.self, "sandeep potrait")
displayKeyboard()
You need to hide or remove the view that you don't want to see in current orientation.
To do that you need to create properties for these views and something like:
var portraitImageView: UIImageView?
var landscapeImageView: UIImageView?
func ... {
if (app.statusBarOrientation.isLandscape) {
var imageView: UIImageView
...
self.view.addSubview(imageView)
self.landscapeImageView = imageView
self.portraitImageView.removeFromSuperview()
self.portraitImageView = nil
} else {
...
}
}

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

How to apply a tintColor to a UIImage?

I have a UIImage that is a small symbol that is all black. The UIImage is getting set in a custom UIButton subclass I have. Is it possible to have the image to apply the tintColor to it, so instead of the black image it changes colors to whatever the tintColor is?
I'm just trying to avoid creating new assets.
// here I want defaultImageName (that is black) to use the tintColor (that is white)
[self setImage:[UIImage imageNamed:defaultImageName] forState:UIControlStateNormal];
If you are just supporting iOS 7 you can use tintColor and UIImageRenderingModeAlwaysTemplate
This article covers that:
https://www.captechconsulting.com/blogs/ios-7-tutorial-series-tint-color-and-easy-app-theming
If you need to support an earlier version you may want to consider this thread
How would I tint an image programmatically on the iPhone?
Swift 4, copy-paste solution
#IBOutlet weak var iconImageView: UIImageView!
iconImageView.image = UIImage(imageLiteralResourceName: "myImageName").withRenderingMode(.alwaysTemplate)
iconImageView.tintColor = UIColor.red
On iOS 13+ you can use the following:
UIImage(named: "img_name")?.withTintColor(.red)
https://developer.apple.com/documentation/uikit/uiimage/3327300-withtintcolor
Try this:
func tinted(with color: UIColor) -> UIImage? {
defer { UIGraphicsEndImageContext() }
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
color.set()
self.withRenderingMode(.alwaysTemplate).draw(in: CGRect(origin: .zero, size: self.size))
return UIGraphicsGetImageFromCurrentImageContext()
}
For example:
button.setImage(UIImage(systemName: "checkmark.circle")?.tinted(with: .systemGray), for: .normal)
Here's how I use tint colors and opacities in IOS 9 with Swift -
//apply a color to an image
//ref - http://stackoverflow.com/questions/28427935/how-can-i-change-image-tintcolor
//ref - https://www.captechconsulting.com/blogs/ios-7-tutorial-series-tint-color-and-easy-app-theming
func getTintedImage() -> UIImageView {
var image :UIImage
var imageView :UIImageView
image = UIImage(named: "someAsset")!
let size : CGSize = image.size
let frame : CGRect = CGRectMake((UIScreen.mainScreen().bounds.width-86)/2, 600, size.width, size.height)
let redCover : UIView = UIView(frame: frame)
redCover.backgroundColor = UIColor.redColor()
redCover.layer.opacity = 0.75
imageView = UIImageView();
imageView.image = image.imageWithRenderingMode(UIImageRenderingMode.Automatic)
imageView.addSubview(redCover)
return imageView
}
Why not use image filtering
btn.setImage(image, for: UIControl.State.normal)
btn.setImage(image.disabled, for: UIControl.State.disabled)
Use CoreImage to do image filter
extension UIImage
{
/// Create a grayscale image with alpha channel. Is 5 times faster than grayscaleImage().
/// - Returns: The grayscale image of self if available.
var disabled: UIImage?
{
// Create image rectangle with current image width/height * scale
let pixelSize = CGSize(width: self.size.width * self.scale, height: self.size.height * self.scale)
let imageRect = CGRect(origin: CGPoint.zero, size: pixelSize)
// Grayscale color space
let colorSpace: CGColorSpace = CGColorSpaceCreateDeviceGray()
// Create bitmap content with current image size and grayscale colorspace
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
if let context: CGContext = CGContext(data: nil, width: Int(pixelSize.width), height: Int(pixelSize.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
{
// Draw image into current context, with specified rectangle
// using previously defined context (with grayscale colorspace)
guard let cg = self.cgImage else{
return nil
}
context.draw(cg, in: imageRect)
// Create bitmap image info from pixel data in current context
if let imageRef: CGImage = context.makeImage(){
let bitmapInfoAlphaOnly = CGBitmapInfo(rawValue: CGImageAlphaInfo.alphaOnly.rawValue)
guard let context = CGContext(data: nil, width: Int(pixelSize.width), height: Int(pixelSize.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfoAlphaOnly.rawValue) else{
return nil
}
context.draw(cg, in: imageRect)
if let mask: CGImage = context.makeImage() {
// Create a new UIImage object
if let newCGImage = imageRef.masking(mask){
// Return the new grayscale image
return UIImage(cgImage: newCGImage, scale: self.scale, orientation: self.imageOrientation)
}
}
}
}
// A required variable was unexpected nil
return nil
}
}
Of course, in Swift 5
Swift5 Extension
extension UIImage {
var template: UIImage? {
return self.withRenderingMode(.alwaysTemplate)
}
}
Usage:
UIImageView
let imgView = UIImageView()
imgView.tintColor = UIColor.red
imgView.image = UIImage(named: "IMAGE_NAME_HERE")?.template
UIButton
let button = UIButton(type: .custom)
button.tintColor = UIColor.red
button.setImage(UIImage(named: "IMAGE_NAME_HERE")?.template, for: .normal)
Use this simple extension to UIImageView
#IBInspectable var tintedColor: UIColor{
get{
return tintColor
}
set{
image = image?.withRenderingMode(.alwaysTemplate)
tintColor = newValue
}
}

Resources