How to tint an animated UIImageView? - ios

I'm attempting to apply tint of a UIImageView consisting of programatically loaded animationImages. Currently using iOS 9.3.
I think I've tried every proposed solution found here for applying the tint including:
Setting the .renderMode on load with: let newImage: UIImage? =
UIImage(named: filename as
String)!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
Setting the tintView either in the Storyboard or programatically
with: zeroImageView.tintColor = UIColor.redColor()
Setting the image .renderMode through the UIImageView itself:
zeroImageView.image = zeroImageView.image!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
Setting the UIImageView's renderMode through the Interface Builder
user defined runtime attributes: imageRenderingMode Number 2
The image sequence is of PNGs with transparency, so, I would like to re-colour the image and maintain the transparency.
I've had no luck and am sort of at a loss. Wondering if maybe these methods only work for a still UIAnimationView? Any help would be greatly appreciated, thanks!
Here's some code:
// Load all images
while let element = enumerator.nextObject() as? String {
if element.hasSuffix("png") {
let filename: NSString = "Digits/0/" + element
print(filename)
imageNames.append(filename as String)
let newImage: UIImage? = UIImage(named: filename as String)!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
images.append(newImage!)
zeroImageView.tintColor = UIColor.redColor()
}
}
// Make animated UIImageView
zeroImageView.userInteractionEnabled = false
zeroImageView.animationImages = images
zeroImageView.animationDuration = 2.0
zeroImageView.startAnimating()
zeroImageView.tintColor = UIColor.redColor()

Try this...
extension UIImage {
func image(withTint tint: UIColor) -> UIImage? {
guard let cgImage = cgImage else {
return nil
}
UIGraphicsBeginImageContextWithOptions(size, false, scale)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
let rect = CGRect(origin: .zero, size: size)
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: 1.0, y: -1.0)
context.setBlendMode(.normal)
context.clip(to: rect, mask: cgImage)
tint.setFill()
context.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}

If none of those worked the next step I would do is programmatically place an empty UIView on top of the UIImageView. First off, it would not interfere with anything going on in the UIImageView.
I would check out this tutorial here: http://www.apptuitions.com/programmatically-creating-uiview-uislider-uiswitch-using-swift/
this will teach you how to programmatically add a UIView to the storyboard. I would add this code in the viewDidLoad() method.
The main thing you need to change is the background color of the UIView. First you have to decide what the color of the tint (which from what I see in your code is red) and then explicitly change the 'Alpha' of the color so that it is barely showing. You change the 'Alpha' because that is basically the opacity of the color.
Hope this helps.

Related

Image isn't bound to the UIImageView

I have a UIImageView in created in Inspector that I resize in my code based on a selected image which i get from the web. However on first load of the image, it's being displayed in the images normal resolution instead of the UIImageViews newly created bounds.
Resizing the UIImageView:
fullScreenImage.bounds.size = CGSize(width: scaledWidth, height: scaledHeight)
Setting the UIImageView's image
let imageStringURL = images[indexPath.row].urls!["regular"]
let imageURL = URL(string: imageStringURL!)!
let imageData = try! Data(contentsOf: imageURL)
let image = UIImage(data: imageData)
fullScreenImage.image = image
This is how it looks when the image is first clicked on to enter "fullscreen mode"
This is how it looks the second time i click it
Not really sure why the Image isn't bounding itself within the specified UIImageView bounds
Instead of resizing the bound, you can set the UIViewContentMode property of UIImageView. This will resize the imageView image to fit inside the bounds.
fullScreenImage.contentMode = .scaleAspectFit
I found a workaround solution. I tried setting the contentMode to aspect fit, and i also tried enabling clip to bounds in the inspector, however none of them worked. So I simply looked into just resizing the UIImage itself and placing it into the UIImageView.
I found an extension in another post for a UIImage that resizes it
extension UIImage{
func resizeImage(newSize: CGSize) -> UIImage {
// Guard newSize is different
guard self.size != newSize else { return self }
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
If anyone knows of a better way please let me know :)

How to use a color as a placeholder in UIImage

How can I use color as a placeholder in UIImage? For example I have an image like this:
And I want to change red to other image and receive something like this:
I thought about checking color of pixels and checking whether it's red and finding a frame, but maybe you know any better methods?
You need to take two View, one is UIView with a backgroundcolor of green(let us say), on that UIView you need to put your UIImageView, and you need to set the constraint according to that. Now set the background color of your UIImageView as red, and as per your requirement, check if the UIImageView's image is nil, if yes then you can add an image to your UIImageView.
Here is a function that will create a UIImage from the specified color. You can call this method with .red to generate the placeholder image you want.
static func image(fromColor color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
let renderer = UIGraphicsImageRenderer(bounds: rect)
let img = renderer.image { ctx in
ctx.cgContext.setFillColor(color.cgColor)
ctx.cgContext.fill(rect)
}
return img
}

Drawing a UILabel with Transformations

I have a UILabel (but this should apply to any UIView). I am trying to draw a label to a UIImage.
First, I've applied a transformation to a label:
let label = UILabel(frame: CGRectMake(0,0,100,100))
label.text = "Fizz Buzz"
label.transform = CGAffineTransformRotate(label.transform, 5)
label.transform = CGAffineTransformScale(label.transform, 2, 2)
label.setTranslation(CGPointMake(10, 15), inView: view)
Then, I want to draw the label to an Image.
UIGraphicsBeginImageContextWithOptions(view.frame.size, false, 0)
let context = UIGraphicsGetCurrentContext()
label.drawTextInRect(view.frame)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Now, the result UIImage that appears is an image of the label with the proper text, but without a the transformation. How do I ensure that the view draws with the transforms?
You need to get the current UIImage data not just the frame to be able to add text to that image. I have a simple function below that adds text to an image. If you are using a UIImageView , you can get the currently assigned image to it, passed it to the function and set the image returned by the function to that UIImageView.
extension UILabel
{
func getRenderedImage() -> UIImage
{
UIGraphicsBeginImageContextWithOptions(self.frame.size, false, 0)
var context = UIGraphicsGetCurrentContext()
CGContextSaveGState(context)
self.layer.renderInContext(context!)
let renderedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return renderedImage
}
}
Sample Usage:
let newImage = label.getRenderedImage()
imagView.image = newImage
To start a small change should be made in the 3rd line of the second code snippet. Replace label.drawTextInRect(view.frame) with label.drawTextInRect(label.frame).
However to properly maintain the transformations, I discovered the easiest way was to create a wrapper UIView. Still apply the transformations to the label, but draw the wrapper view to a UIImage instead of the label. Use the line:
wrapperView.drawViewHierarchyInRect(wrapperView.frame, afterScreenUpdates: true)

How can I change the color of the UITabBar top border by creating a UIImage programmatically?

From my understanding, the only way to change the color of the top border is to set the background image (320x49, with pixel line at top). It seems to me that this is the only way (please correct me if I'm wrong).
Is there a way to do this without using an image file? For example, someone helped me change the NavigationBar bottom border by creating a UIImage from code:
UINavigationBar.appearance().shadowImage = UIImage.colorForNavBar(UIColor.redColor())
extension UIImage {
class func colorForNavBar(color: UIColor) -> UIImage {
let rect = CGRectMake(0.0, 0.0, 1.0, 1.0)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillRect(context, rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
This solution actually works well; it changes the color of my bottom border.
I tried to apply this to the TabBar, but nothing changes at all.
UITabBar.appearance().shadowImage = UIImage.colorForNavBar(.redColor())
You've pretty much answered your own question. You can do the same thing with your UITabBar as you did with your UINavigationBar. If you want to change the shadow image (i.e. the "top border"), then you have to change the background image. Straight from Apple:
The custom shadow image for the tab bar. This attribute is ignored if the tab bar does not also have a custom background image. To set this attribute programmatically, use the shadowImage property.
In your own question you seem to be aware of this:
the only way to change the color of the top border is to set the background image (320x49, with pixel line at top)
Except that it's not the background image that has a line at the top. You just have to set the background image to anything, then you can set the shadow image to your preference.
If you open up the simple "tabbed application" template within Xcode, you'll find that adding these two lines of code (and your UIImage extension code) indeed work:
// White background with red border on top
UITabBar.appearance().backgroundImage = UIImage.colorForNavBar(.whiteColor())
UITabBar.appearance().shadowImage = UIImage.colorForNavBar(.redColor())
Here is the Swift 3 solution:
extension UIImage {
class func colorForNavBar(color: UIColor) -> UIImage {
let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
// Or if you need a thinner border :
// let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 0.5)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(color.cgColor)
context!.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
used with the code above in the viewDidLoad of the UITabBarController
UITabBar.appearance().backgroundImage = UIImage.colorForNavBar(color: .white)
UITabBar.appearance().shadowImage = UIImage.colorForNavBar(color: .red)
You need to provide an different image for UINavigationBar.appearance().backgroundImage.
For example:
UINavigationBar.appearance().backgroundImage = UIImage.colorForNavBar(.blackColor())
UINavigationBar.appearance().shadowImage = UIImage.colorForNavBar(.redColor())
Normally, the other answers got it right - you have to set both a background image and a shadow image. However, doing so will cause the bar to drop its translucency (blur); even if you set a transparent image, the bar will be transparent, not translucent.
We also had a similar need, but we wanted to preserve the translucency of the bar. Instead of setting a shadow image, we subclassed the bar, and put a hairline subview with a color we want. When the bar lays out its subviews, we set the frame of the hairline to be the width of the bar, and a pixel exactly.
See my GitHub demo project.
Here is a screenshot of the result:
After you include my subview in your project, just use the following line to set the color:
if let tabBar = tabBarController?.tabBar as? ColoredHairlineTabBar {
tabBar.hairlineColor = ... //Your color
}
What about simply subclassing UITabBar and adding a new sublayer to the view in layoutSubviews.
Swift example:
override func layoutSubviews() {
super.layoutSubviews()
let topBorder = CALayer()
let borderHeight: CGFloat = 2
topBorder.borderWidth = borderHeight
topBorder.borderColor = UIColor.redColor().CGColor
topBorder.frame = CGRect(x: 0, y: -1, width: self.frame.width, height: borderHeight)
self.layer.addSublayer(topBorder)
}
You don't need an extension to create an image of a certain size, UIImage has a perfectly good constructor for that.
To prevent losing the translucent blur, you can set the bar tint color instead of a background image. You can also use the screen scale to make sure the border is one pixel, like the original border was:
let hairlineHeight = CGFloat(1) / UIScreen.main.scale
tabBar.barTintColor = .white
tabBar.shadowImage = UIImage(color: .black, size: CGSize(width: 1, height: hairlineHeight))

Remove mask from deselected tabs UITabBarItem Swift

I am trying to implement an UITabBarController with 2 UITabBarItems . I added in storyboard the TabBarController. I almost did it, but still I am blocked with 2 important issues:
1) Here is how tab bar should look:
Please ignore orange button, that is not a tabItem.
So I put 2 tabItems , and I want to keep white images for both tabs even if one of them is selected.
I checked a lot of times with tintColor, barTintColor and no success.
Also I tried to set tabBarItem in ViewController:
override func awakeFromNib() {
super.awakeFromNib()
let imgHome = UIImage(named: "btnHome")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
let imgProfile = UIImage(named: "btnProfile")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
let imgSelectedTab = UIImage(named: "selectedTab_imgBackground")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
tabBarItem = UITabBarItem(title: nil, image: imgProfile, selectedImage: imgSelectedTab)
}
but no success. Any thoughts at this issue ?
2) Second issue is about selectedImage property of UITabBarItem class.
The width of image does not fit the tab. I changed between devices, and for every device the selected image is over the other tab, or does not fit the current tab.(I found a solution: to have the same image but with different width for every device. But for sure this is not a good solution)
Any kind of help will be fine!
Many thanks
You need to change you rendering mode to UIImageRenderingModeAlwaysOriginal instead of automatic.
Here is a full example of how I managed both issues:
https://github.com/AndreiBoariu/TabBarController
For first issue, I solved using this for loop in UITabBarController class:
for item in tabBar.items as! [UITabBarItem] {
if let image = item.image {
item.image = image.imageWithColor(UIColor.whiteColor()).imageWithRenderingMode(.AlwaysOriginal)
}
}
and here is the extension of UIImage
public extension UIImage {
func imageWithColor(tintColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
let context = UIGraphicsGetCurrentContext() as CGContextRef
CGContextTranslateCTM(context, 0, self.size.height)
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, kCGBlendModeNormal)
let rect = CGRectMake(0, 0, self.size.width, self.size.height) as CGRect
CGContextClipToMask(context, rect, self.CGImage)
tintColor.setFill()
CGContextFillRect(context, rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
return newImage
}
}
For second issue, check code from github ;)

Resources