How can I change the top border of my UITabBar? - ios

I'd like the UITabBar to have a top border of width 5.0. The border should be yellow color.
I don't want any left/bottom/right borders.
The Tab Bar border should be flat (no shadows or anything like that).
How can I remove shadow (image) line?

You can hide the top border this way in your FirstViewController.swift:
self.tabBarController!.tabBar.layer.borderWidth = 0.50
self.tabBarController!.tabBar.layer.borderColor = UIColor.clear.cgColor
self.tabBarController?.tabBar.clipsToBounds = true
And result will be:
Before:
After:
Hope it helps.
EDIT:
You can set background image this way:
UITabBar.appearance().backgroundImage = UIImage(named: "yourImageWithTopYellowBorder.png")

If you want to completely remove tab bar, put this in your AppDelegate:
UITabBar.appearance().shadowImage = UIImage()
UITabBar.appearance().backgroundImage = UIImage()

This is how I get it done. I added a subview on top of the UITabBar.
let lineView = UIView(frame: CGRect(x: 0, y: 0, width:tabBarController.tabBar.frame.size.width, height: 1))
lineView.backgroundColor = UIColor.yellow
tabBarController.tabBar.addSubview(lineView)

This is the complete solution, compiled of different SO answers, that worked for me (Swift 3):
// The tabBar top border is done using the `shadowImage` and `backgroundImage` properties.
// We need to override those properties to set the custom top border.
// Setting the `backgroundImage` to an empty image to remove the default border.
tabBar.backgroundImage = UIImage()
// The `shadowImage` property is the one that we will use to set the custom top border.
// We will create the `UIImage` of 1x5 points size filled with the red color and assign it to the `shadowImage` property.
// This image then will get repeated and create the red top border of 5 points width.
// A helper function that creates an image of the given size filled with the given color.
// http://stackoverflow.com/a/39604716/1300959
func getImageWithColor(color: UIColor, size: CGSize) -> UIImage
{
let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size.width, height: size.height))
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
// Setting the `shadowImage` property to the `UIImage` 1x5 red.
tabBar.shadowImage = getImageWithColor(color: UIColor.red, size: CGSize(width: 1.0, height: 5.0))

SWIFT 3
I needed border colors (and line colors and weights) to match other elements in my app, so this worked for me in my custom UITabBarController's viewDidLoad:
tabBar.layer.borderWidth = 0.3
tabBar.layer.borderColor = UIColor(red:0.0/255.0, green:0.0/255.0, blue:0.0/255.0, alpha:0.2).cgColor
tabBar.clipsToBounds = true

UIView *borderLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 5.0)];
borderLine.backgroundColor = [UIColor yellowColor];
[self.tabBarController.tabBar addSubview:borderLine];
This is the way to add border to a UITabBar which I follow.
It works cool.

Swift 4.2
self.tabBarController!.tabBar.layer.borderWidth = 0.50
self.tabBarController!.tabBar.layer.borderColor = UIColor.clear.cgColor
self.tabBarController?.tabBar.clipsToBounds = true
Just change border color as you want.

There is a property named shadowImage that was introduced in iOS 6. You can change this to change the top border. For example, you can use a 1x1px image with a single colour to change the top border to that colour:
UITabBar.appearance().shadowImage = UIImage(named: "TabBarShadow")
You can also set it as just UIImage() to completely remove the top border.
UITabBar.appearance().shadowImage = UIImage()
To answer your question of a 5px border, this can be done by using a 1x5px image. There does not appear to be a limit on the size of the image and it will just repeat (so you could have a dotted line for example by having a 4x5px image where the first 2x5px are black and the next 2x5px are transparent). Note that if you use this, it is outside the bounds of the UITabBar so content will go behind the image unless you change the view bounds.

It's a shadow image (property) of tabbar. Try following solutions and see.
** Swift **
//Remove shadow image by assigning nil value.
UITabBar.appearance().shadowImage = nil
// or
// Assing UIImage instance without image reference
UITabBar.appearance().shadowImage = UIImage()
** Objective-C **
//Remove shadow image by assigning nil value.
[[UITabBar appearance] setShadowImage: nil];
// or
// Assing UIImage instance without image reference
[[UITabBar appearance] setShadowImage: [[UIImage alloc] init]];
Here is apple guideline for shadowImage.
#available(iOS 6.0, *)
open var shadowImage: UIImage?
Default is nil. When non-nil, a custom shadow image to show instead of
the default shadow image. For a custom shadow to be shown, a custom
background image must also be set with -setBackgroundImage: (if the
default background image is used, the default shadow image will be
used).

Firstly create an extension of UIImage as follow
extension UIImage {
func createSelectionIndicator(color: UIColor, size: CGSize, lineWidth: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(CGRect(x: 0, y: 0, width: size.width, height: lineWidth))
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
In your view controller add the following code.
let tabBar = self.tabBarController!.tabBar
tabBar.selectionIndicatorImage = UIImage().createSelectionIndicator(color: UIColor.blue, size: CGSize(width: tabBar.frame.width/CGFloat(tabBar.items!.count), height: tabBar.frame.height) , lineWidth: 5.0)

Just set the UITabBar backgroundImage and shadowImage to be clear color:
tabBar.shadowImage = UIImage.init(color: UIColor.clear)
tabBar.backgroundImage = UIImage.init(color: UIColor.clear)

Related

Adding UIImage ignores and resizes UIImageView frame

I'm currently trying to add an image into the navigation item for one view. In the view's viewDidLoad(), a function is called with the following code, similar to this post:
let logo = UIImage(named: "Menu_Logo")
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 122, height: 26))
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.image = logo
self.navigationItem.titleView = imageView
Instead of giving me the expected size however, the view ends up looking like this:
Removing the UIImage from the UIImageView makes the view sized correctly like this:
This seems like strange behaviour to me, especially since I did set the content mode to .scaleAspectFit. Is there something I am forgetting regarding adding an UIImageView as the navigationItem.titleView?
On UINavigationBar, title view takes its full size, if content is large.
Resize the image rather than UIImageView as following with passing size (122, 26). This will solve your problem.
func imageResize(sizeChange: CGSize) -> UIImage {
let hasAlpha = true
let scale: CGFloat = 0.0 // Use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(sizeChange, !hasAlpha, scale)
self.draw(in: CGRect(origin: CGPoint.zero, size: sizeChange))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
return scaledImage!
}

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
}

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

How to change the color of the bottom border of UINavigationBar?

I read many threads, but none solved this question in a clear, consistent answer for the latest version of Swift.
For example, this question's top answer suggests UINavigationBar.appearance().setShadowImage(). However, such a method does not exist in the latest version of swift.
I don't want to hide the bottom border. I just want to change the color.
Additionally, it'd be great to be able to change the height, but I know I'm asking too much in one question.
Edit: I created a 2x1 pixel image and set it to the shadowImage, but the border remains unchanged:
UINavigationBar.appearance().barTintColor = UIColor.whiteColor()
UINavigationBar.appearance().shadowImage = UIImage(named: "border.jpg") //in my AppDelegate, for global appearance
Here's the image; it's really small:
SWIFT 2.x :
Out of convenience, I've extended UIImage() to allow me to essentially use it as a color with the code immediately below.
extension UIImage {
class func imageWithColor(color: UIColor) -> UIImage {
let rect = CGRectMake(0, 0, 1.0, 0.5)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Next, you'll want to add the following lines to your code to adjust the viewController's UINavigationBar's shadow image, or color in this instance.
// Sets Bar's Background Image (Color) //
self.navigationController?.navigationBar.setBackgroundImage(UIImage.imageWithColor(UIColor.blueColor()), forBarMetrics: .Default)
// Sets Bar's Shadow Image (Color) //
self.navigationController?.navigationBar.shadowImage = UIImage.imageWithColor(UIColor.redColor())
SWIFT 3.x / 4.x :
Extension code:
extension UIImage {
class func imageWithColor(color: UIColor) -> UIImage {
let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 0.5)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
NavigationBar code:
// Sets Bar's Background Image (Color) //
navigationController?.navigationBar.setBackgroundImage(UIImage.imageWithColor(color: .blue), for: .default)
// Sets Bar's Shadow Image (Color) //
navigationController?.navigationBar.shadowImage = UIImage.imageWithColor(color: .red)
Edit 1:
Updated extension code so you can adjust rect size without changing UIImage color opacity.
Edit 2:
Added Swift 3 + Swift 4 code.
Old UIKit setter methods like UISomeClass.setSomething(whatIWantToSet) have been reformulated so that you can directly set them with an = sign. So, in my example you would have to use UISomeClass.something = whatIWantToSet.
In your case, it's UINavigationBar.appearance().shadowImage = whatYouWantToSet.
A bit tricky solution, but it works with no coding and extensions needed. Just add in StoryBoard under your navigationBar a Progress View and set its color and height to whatever you like. If you want full border also set progress to 1.
So you will get a proper border for your navigation bar and an option to add progressView at any time as extra bonus.
I know, I know... it's kinda tricky, but programmers should be lazy, no?

How do I style a button to have transparent text?

I am currently working on a welcome page and I came across this interesting design:
I'm trying to make a button at the bottom with text that goes through the semi-transparent view behind it. I tried out the following code but it doesn't seem to be working:
let transparent = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
let semiwhite = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.15)
bottomView.backgroundColor = semiwhite
loginButton.backgroundColor = semiwhite
loginButton.setTitleColor(transparent, forState: .Normal)
loginButton.layer.cornerRadius = 10
loginButton.layer.masksToBounds = true
loginButton.layer.borderWidth = 2.5
loginButton.layer.borderColor = transparent.CGColor
Does anyone know how to do this?
I'd suggest that you don't want "transparent" text. Instead, you may want to think of this as a white view with a "mask" which is the outline of the text, allowing the image underneath to be revealed. This is complicated a bit because the mask is actually inverted from how we generally mask images (e.g., the "Sign in with Facebook" text is not the mask, but rather the white space around it is). But Core Graphics offer ways to do this very easily.
So, while it's probably easiest to create this graphic in your favorite image editing tool, if you wanted to do it programmatically, you could do something like the following in Swift 3 or later:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let image1 = maskedImage(size: button1.bounds.size, text: "Sign in with Facebook")
button1.setImage(image1, for: .normal)
let image2 = maskedImage(size: button2.bounds.size, text: "Sign-up with Email")
button2.setImage(image2, for: .normal)
}
func maskedImage(size: CGSize, text: String) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, true, 0)
let context = UIGraphicsGetCurrentContext()
context?.scaleBy(x: 1, y: -1)
context?.translateBy(x: 0, y: -size.height)
// draw rounded rectange inset of the button's entire dimensions
UIColor.white.setStroke()
let pathRect = CGRect(origin: .zero, size: size).insetBy(dx: 10, dy: 10)
let path = UIBezierPath(roundedRect: pathRect, cornerRadius: 5)
path.lineWidth = 4
path.stroke()
// draw the text
let attributes: [NSAttributedStringKey: Any] = [
.font: UIFont.preferredFont(forTextStyle: .caption1),
.foregroundColor: UIColor.white
]
let textSize = text.size(withAttributes: attributes)
let point = CGPoint(x: (size.width - textSize.width) / 2, y: (size.height - textSize.height) / 2)
text.draw(at: point, withAttributes: attributes)
// capture the image and end context
let maskImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// create image mask
guard let cgimage = maskImage?.cgImage, let dataProvider = cgimage.dataProvider else { return nil }
let bytesPerRow = cgimage.bytesPerRow
let bitsPerPixel = cgimage.bitsPerPixel
let width = cgimage.width
let height = cgimage.height
let bitsPerComponent = cgimage.bitsPerComponent
guard let mask = CGImage(maskWidth: width, height: height, bitsPerComponent: bitsPerComponent, bitsPerPixel: bitsPerPixel, bytesPerRow: bytesPerRow, provider: dataProvider, decode: nil, shouldInterpolate: false) else { return nil }
// create the actual image
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
UIGraphicsGetCurrentContext()?.clip(to: rect, mask: mask)
UIColor.white.withAlphaComponent(0.9).setFill()
UIBezierPath(rect: rect).fill()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// return image
return image
}
The technique to pay attention to here is the use of CGImage(maskWidth:...), which lets us create an image mask out everything except the white text and border that we drew. Then when we create the final image, we can clip it to this "image mask" with clip(to:mask:).
Generally, when we talk about masking images, we're masking them with UIBezierRect or other images (where a non-zero alpha channel reveals what should be masked and what shouldn't). But here we're masking using an Core Graphics "image mask", which gives us more control. For a discussion on the differences between masking with an image mask and masking with an image, see the Masking Images chapter of the Quartz 2D Programming Guide.
For Swift 2 rendition, see previous revision of this answer.
I would design the button in something like Adobe Fireworks, leaving the text transparent and then save it as a GIF.
You can then set it as the background to your button.
First thing you can set tintcolor as your desired color.
and if you are setting alpha to 0 then that means view is being hidden. so don't give alpha to 0.
You have option like, you can use clear color. so background color will show.
second thing if you want to use color then take white color with alpha 0.5 or other color with alpha 0.5 or 0.2 likewise don't take exact 0. it will make view invisible.
This was fundamental part. Now in your case you can do something like this,
for example your button size is 30 x 100 than you should take uiview as background with greater size of that button and set clear color as background color and add button on that view so border color or textcolor will automatically set like wise if you give clear color as tint or border.
another easy option is you can take image exactly matching with requirement and take imageview and set background to clear color and image as your image and add button on that image inplace off button with clear color and no text because text covers in image itself
hope this will help :)

Resources