UIButton border on right and left sides - ios

I've been having trouble getting a border or shadow between three buttons to show some separation. I have tried getting a border or shadow on just the left and right side of the middle button but I could only get the shadow on one side. Any help is greatly appreciated.
What I have tried to user to get a shadow but only shows on right side:
middleButton.layer.backgroundColor = UIColor.whiteColor().CGColor
middleButton.layer.borderColor = UIColor(red: 208/255, green: 208/255, blue: 208/255, alpha: 1.0).CGColor
middleButton.layer.borderWidth = 0.0
middleButton.layer.masksToBounds = false
middleButton.layer.shadowColor = UIColor(red: 208/255, green: 208/255, blue: 208/255, alpha: 1.0).CGColor
middleButton.layer.shadowOffset = CGSizeMake(0.5, 1.0)
middleButton.layer.shadowOpacity = 1.0
middleButton.layer.shadowRadius = 1.0
Below is my current view with the three buttons that I am trying to add a separator between:
The hierarchy I have for it in a table cell:

Swift 3 Answer
You can make an extension for UIButton
extension UIButton {
func addRightBorder(borderColor: UIColor, borderWidth: CGFloat) {
let border = CALayer()
border.backgroundColor = borderColor.cgColor
border.frame = CGRect(x: self.frame.size.width - borderWidth,y: 0, width:borderWidth, height:self.frame.size.height)
self.layer.addSublayer(border)
}
func addLeftBorder(color: UIColor, width: CGFloat) {
let border = CALayer()
border.backgroundColor = color.cgColor
border.frame = CGRect(x:0, y:0, width:width, height:self.frame.size.height)
self.layer.addSublayer(border)
}
}
Then you can use this for any of your buttons.
middleButton.addRightBorder(borderColor: UIColor.white, borderWidth: 1.0)
middleButton.addLeftBorder(borderColor: UIColor.white, borderWidth: 1.0)
Should work fine. Happy Coding !

Where there are limits on the actual code possibilities, the solution lay with the graphic icons you have for 'Favourite', 'Share', Map'.
A separator border / line between the buttons can be done in the graphic icon creation for the buttons you have. Adobe illustrator or photoshop. I have recreated a sample in illustrator, brought it over to 'Assets.xcassets' as an image and added it as background image to the button.
Button e.g.
Snapshop of simulator as follows...

Another approach is to add your buttons to UIStackView. With it you can easily manage how you want the subviews to be arranged and distributed.
Add UIView's as separators with a width constraint of 1 point. After setting your constraints on the UIStackView, set distribution property to "Equal Spacing" and the views will align according to it.

Related

How to implement a black gradient on an image in iOS using swift

I am working on an iOS project where I have white labels on bright images. The problem is for bright images the white labels are not showing. Here is an example:
Label not showing: https://imgur.com/hKtejHn
Label showing: https://imgur.com/Ef5qJAh
I think if I add a black gradient on all the image then the white labels will be visible. Can anyone help me as to how to implement the solution in Swift?
Thank!
If you want to add gradient on your imageView then you can just implement CAGradientLayer on your imageView.layer.
Try to change some values for your own custom look, but the code below is pretty much it.
let gradientLayer = CAGradientLayer()
gradientLayer.frame = imageView.frame
let colors = [
UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor,
UIColor(red: 0, green: 0, blue: 0, alpha: 0).cgColor
]
gradientLayer.startPoint = CGPoint(x: 0.1, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.9, y: 0.5)
gradientLayer.colors = colors
imageView.layer.addSublayer(gradientLayer)
You can change colors, add colors, change start/end-points. You can find a lot of different CAGradientLayer-guides on youtube or google.
try this:
extension UILabel {
func lblShadow(color: UIColor , radius: CGFloat, opacity: Float){
self.textColor = color
self.layer.masksToBounds = false
self.layer.shadowRadius = radius
self.layer.shadowOpacity = opacity
self.layer.shadowOffset = CGSize(width: 1, height: 1)
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}
usage:
label.lblShadow(color: UIColor.white, radius: 3, opacity: 0.75)

Swift Button.setText doesn't work with gradient

I'm new in Swift and I have a problem. my button do not set the text if I try to use the gradient. here is my code:
let gradient:CAGradientLayer = CAGradientLayer()
let colorBottom = UIColor(red: 9/255.0, green: 137/255.0, blue: 133/255.0, alpha: 1.0).CGColor
let colorTop = UIColor(red: 222.0/255.0, green: 255.0/255.0, blue: 201.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = LoginButton.bounds
gradient.cornerRadius = 25.0
LoginButton.layer.addSublayer(gradient)
LoginButton.setTitle("Login", forState: UIControlState.Normal)
LoginButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
self.view.addSubview(LoginButton)
Adding a sublayer works the same as adding a subview so adding the gradient layer may be covering the other layers where the text is drawn.
If you remove this line does it work?
LoginButton.layer.addSublayer(gradient)
If it does, try seeing how many layers are in LoginButton.layer.sublayers and try inserting the gradient layer behind one of those sublayers (wherever it looks the best) using one of the following methods:
-insertSublayer:above:
-insertSublayer:below:
-insertSublayer:atIndex:
This line is the problem:
LoginButton.layer.addSublayer(gradient)
Do not add any extra layers to a UIButton. It already knows how to draw itself. If you want it to have a gradient background, draw the gradient into an image and set that as the button's background image in the normal way.
Or make the button clear and put the gradient layer behind the button (though this is not as good).

Alpha component on UIBezierPath Fill

I have a view defined like this :
class TriangleView: UIView {
override func drawRect(rect: CGRect) {
// Get Height and Width
let layerHeight = self.layer.frame.height
let layerWidth = self.layer.frame.width
// Create Path
let bezierPath = UIBezierPath()
bezierPath.moveToPoint(CGPointMake(0, 0))
bezierPath.addLineToPoint(CGPointMake(0, layerHeight))
bezierPath.addLineToPoint(CGPointMake(layerWidth, layerHeight + 4))
bezierPath.addLineToPoint(CGPointMake(0, 0))
bezierPath.closePath()
// Apply Color
UIColor(red: (69/255.0), green: (209/255.0), blue: (153/255.0), alpha: 1.0).setFill()
bezierPath.fill()
// Mask to Path
let shapeLayer = CAShapeLayer()
shapeLayer.borderColor = UIColor.clearColor().CGColor
shapeLayer.path = bezierPath.CGPath
self.layer.mask = shapeLayer
}
}
I'm trying to make it semi-transparent, however using UIColor(red: (69/255.0), green: (209/255.0), blue: (153/255.0), alpha: 0.5).setFill() as well as using bezierPath.fillWithBlendMode( .Normal, alpha: 0.5) produces the same result of a darker than normal color with no transparency, the more I decrease the alpha in both cases, the darker the color becomes. I'm not sure what I'm doing wrong.
Set the views backgroundColor to UIColor.clearColor().
I think you are assigning the TriangleView for the main view which present on the ViewController. Instead of that drag another view from the Object Library and assign it as TriangleView

How to draw a rectangle inside a tableviewcell

Probably it's a weird question, but I gave up. Situation: I have a TableView with a prototype cell. Inside the cell (I have a custom class) I want to have a background, but not the whole cell, and a label on it. The size of the background would be changed when I know the length of the text. The background is a rectangle, with a gradient fill, corner radius, gradient fill. How should I draw this rect? Should I do it with UIKit or with CoreGraphics? My first thought was to import the image but because i have to make it bigger if there is a long text, I decided to make it programmaticaly. Thanks in advance.
Try this solution:
// Add a label with sizeToFit
let label = UILabel()
label.text = "Add some text that you want"
label.sizeToFit()
// Add a rectangle view
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: label.frame.size.width, height: 40))
// Add gradient
let gradientLayer = CAGradientLayer()
gradientLayer.frame = rectangle.bounds
let color1 = UIColor.yellowColor().CGColor as CGColorRef
let color2 = UIColor(red: 1.0, green: 0, blue: 0, alpha: 1.0).CGColor as CGColorRef
let color3 = UIColor.clearColor().CGColor as CGColorRef
let color4 = UIColor(white: 0.0, alpha: 0.7).CGColor as CGColorRef
gradientLayer.colors = [color1, color2, color3, color4]
gradientLayer.locations = [0.0, 0.25, 0.75, 1.0]
rectangle.layer.addSublayer(gradientLayer)
// Add corner radius
gradientLayer.cornerRadius = 10
// Add the label to your rectangle
rectangle.addSubview(label)
// Add the rectangle to your cell
cell.addSubview(rectangle)

CALayer not resizing with Autolayout

I have created a progress bar to be used in a tableView by creating a gradient layer. It works perfectly.
iPhone5:
In order to use the app on multiple devices, I have created the UIView in Storyboard, tagged it and added constraints.
However, when I use the app on an iPhone 6 the CALayer don't resize.
iPhone6:
I find this extremely stupid, but never mind. I have looked around and tried to understand how to solve this for months, but I have come up short. Does ANYONE know how to make CALayers resize with the UIView? Any help would be very much appreciated ! Thank you.
progressBar = cell.contentView.viewWithTag(3) as UIView!
progressBar.layer.cornerRadius = 4
progressBar.layer.masksToBounds = true
// create gradient layer
let gradient : CAGradientLayer = CAGradientLayer()
// create color array
let arrayColors: [AnyObject] = [
UIColor (red: 255/255, green: 138/255, blue: 1/255, alpha: 1).CGColor,
UIColor (red: 110/255, green: 110/255, blue: 118/255, alpha: 1).CGColor]
// set gradient frame bounds to match progressBar bounds
gradient.frame = progressBar.bounds
// set gradient's color array
gradient.colors = arrayColors
//Set progress(progressBar)
var percentageCompleted = 0.6
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.locations = [percentageCompleted, percentageCompleted]
gradient.endPoint = CGPoint(x: 1, y: 0.5)
// replace base layer with gradient layer
progressBar.layer.insertSublayer(gradient, atIndex: 0)
The default layer of a UIView does resize with its view, but sublayers don't (as you found out). One way to make this work is to create a custom view class, move the code you have in your question to it, and override layoutSublayersOfLayer where you can set the gradient layer to be the same size as the view. Because this code is now in a custom class, I also created a property percentageCompleted (instead of a local variable), and added a willSet clause so the bar's appearance is updated any time you change the percentageCompleted property.
class RDProgressView: UIView {
private let gradient : CAGradientLayer = CAGradientLayer()
var percentageCompleted: Double = 0.0 {
willSet{
gradient.locations = [newValue, newValue]
}
}
override func awakeFromNib() {
self.layer.cornerRadius = 4
self.layer.masksToBounds = true
// create color array
let arrayColors: [AnyObject] = [
UIColor (red: 255/255, green: 138/255, blue: 1/255, alpha: 1).CGColor,
UIColor (red: 110/255, green: 110/255, blue: 118/255, alpha: 1).CGColor]
// set gradient's color array
gradient.colors = arrayColors
//Set progress(progressBar)
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.locations = [percentageCompleted, percentageCompleted]
gradient.endPoint = CGPoint(x: 1, y: 0.5)
self.layer.insertSublayer(gradient, atIndex: 0)
}
override func layoutSublayersOfLayer(layer: CALayer!) {
super.layoutSublayersOfLayer(layer)
gradient.frame = self.bounds
}
}
In IB, you would change the class of your view to RDProgressView (in my example), and in cellForRowAtIndexPath, you would only need to get a reference to the view, and set its percentageCompleted property.
progressBar = cell.contentView.viewWithTag(3) as RDProgressView!
progressBar.percentageCompleted = 0.2
As an alternative to the accepted answer, you could also change the views layer class to be CAGradientLayer. The views layer will always be resized according to layout changes. You can achieve that by subclassing UIView
class GradientView: UIView {
override class func layerClass() -> AnyClass {
return CAGradientLayer.self
}
}
then set the colors
if let gradientLayer = gradientView.layer as? CAGradientLayer {
gradientLayer.colors = arrayColors
}
It's less code than adding and maintaining a sublayer, but might not suit all use cases.

Resources