Get color behind an object - ios

I want to invert a button that looks like the following:
Inverting, in this case, would mean making the title blue and the button's background white. Since I do not want to hard-code it, I cannot simply set it blue. When I set the background color to white, and the title's color to clearColor, of course, the title is not readable anymore:
Is there a way to find out the color that is behind the button (which is a background image of the view currently), so I can set the title's color to that color?
Thanks in advance :)

Following on from Kjuly's answer, you should create a UIButton subclass to do this.
In order to apply your different styles, you just want to add some action listeners for the button touch events in order to re-draw your button when the state changes.
As Kjuly says, you'll then want to override drawRect in order to do your custom button drawing, depending on whether the button is pressed or not.
Something like this should achieve what you're after.
class CustomButton: UIButton {
private let paragraphStyle = NSMutableParagraphStyle()
private var titleAttributes = [String:AnyObject]()
private var isPressed = false
var highlightColor = UIColor.whiteColor() { // stroke & highlight color of the button
didSet {
setNeedsDisplay()
}
}
var cornerRadius:CGFloat = 10.0 { // corner radius of button
didSet {
setNeedsDisplay()
}
}
var strokeWidth:CGFloat = 5.0 { // stroke width of button
didSet {
setNeedsDisplay()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
addTarget(self, action: Selector("buttonWasPressed"), forControlEvents: .TouchDown)
addTarget(self, action: Selector("buttonWasReleased"), forControlEvents: .TouchUpInside)
addTarget(self, action: Selector("buttonWasReleased"), forControlEvents: .TouchDragExit)
addTarget(self, action: Selector("buttonWasReleased"), forControlEvents: .TouchCancel)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func buttonWasPressed() {
isPressed = true
setNeedsDisplay() // set button to be redrawn
}
func buttonWasReleased() {
isPressed = false
setNeedsDisplay() // set button to be redrawn
}
override func drawRect(rect: CGRect) {
let size = bounds.size
let context = UIGraphicsGetCurrentContext()
if isPressed { // button pressed down
let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius) // path of button shape for filling
CGContextSetFillColorWithColor(context, highlightColor.CGColor) // white background of the button
path.fill() // fill path
CGContextSetBlendMode(context, .DestinationOut) // set blend mode for transparent label
} else { // button not pressed
let path = UIBezierPath(roundedRect: UIEdgeInsetsInsetRect(bounds, UIEdgeInsets(top: strokeWidth*0.5, left: strokeWidth*0.5, bottom: strokeWidth*0.5, right: strokeWidth*0.5)), cornerRadius: cornerRadius-strokeWidth*0.5) // path of button shape for stroking
CGContextSetStrokeColorWithColor(context, highlightColor.CGColor) // set stroke color
path.lineWidth = strokeWidth
path.stroke()
}
guard let label = titleLabel else {return} // title label
guard let text = label.text else {return} // text to draw
// update text attributes, add any extra attributes you want transferred here.
paragraphStyle.alignment = label.textAlignment
titleAttributes[NSFontAttributeName] = label.font
titleAttributes[NSParagraphStyleAttributeName] = paragraphStyle
titleAttributes[NSForegroundColorAttributeName] = label.textColor
let textHeight = text.sizeWithAttributes(titleAttributes).height // heigh of the text to render, with the attributes
let renderRect = CGRect(x:0, y:(size.height-textHeight)*0.5, width:size.width, height:size.height) // rect to draw the text in
text.drawInRect(renderRect, withAttributes: titleAttributes) // draw text
}
}
You can then use this button by setting the various attributes on the button's titleLabel, as well the strokeWidth and cornerRadius property. For example:
let button = CustomButton(frame: CGRect(x: 50, y: 50, width: 200, height: 75))
button.titleLabel?.text = "Let's go!"
button.titleLabel?.font = UIFont.systemFontOfSize(30)
button.titleLabel?.textAlignment = .Center
button.titleLabel?.textColor = UIColor.whiteColor()
button.cornerRadius = 15.0
button.strokeWidth = 5.0
view.addSubview(button)

You need to create a subclass of the UIButton (or just UIControl), and override the -drawRect: to do rending w/ custom blend mode (kCGBlendModeDestinationOut):
- (void)drawRect:(CGRect)rect
{
[[UIColor whiteColor] setFill]; // white background of the button
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:5.f];
[path fill];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetBlendMode(context, kCGBlendModeDestinationOut); // Set blend mode
// u'd better to cache this attributes if u need to redraw the button frequently.
NSMutableParagraphStyle * paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.alignment = NSTextAlignmentCenter;
NSDictionary * attributes = #{NSFontAttributeName:[UIFont systemFontOfSize:20.f], NSParagraphStyleAttributeName:paragraphStyle};
[#"Let's Go" drawAtPoint:CGPointMake(0.f, 0.f) withAttributes:attributes];
CGContextRestoreGState(context);
}
The kCGBlendModeDestinationOut is the blend mode you need to set, which will just show the view below current one (R = D*(1 - Sa)), in your case, the background image.
And about blend mode:
... R, S, and D are, respectively,
premultiplied result, source, and destination colors with alpha; Ra,
Sa, and Da are the alpha components of these colors.
The Porter-Duff "source over" mode is called `kCGBlendModeNormal':
R = S + D*(1 - Sa)
Note that the Porter-Duff "XOR" mode is only titularly related to the
classical bitmap XOR operation (which is unsupported by
CoreGraphics).

If your UIButton sits in a UIView and You want to access the UIView's backgroundColor, you can do so my referencing the UIButton's superclass.
Example:
#IBOutlet weak var myButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
myButton.backgroundColor = myButton.superview?.backgroundColor
}
If you want to use the color of a Background Image you might want to have a look into CoreImage to get the average or perPixel Color of that image.

Related

Is is possible to animate text color changing only in a part of text in iOS?

I wonder if it even possible in iOS to animate changing color in only a part of the text, preferably not char by char, but pixel by pixel, like on this picture?
I know how to change text color in static with NSAttributedString and I know how to animate the whole text with CADisplayLink, but this makes me worry.
Maybe I can dive into CoreText, but I'm still not sure it is possible even with it. Any thoughts?
UPD I decided to add a video with my first results to make the question more clear:
my efforts for now (the label is overlapping)
You can quite easily achieve this using CoreAnimation possibilities.
I've added a simple demo, you play with it here (just build the project and tap anywhere to see the animation).
The logic is the following:
Create a custom subclass of UIView.
When some text is set, create two similar CATextLayers, each with the same text and frame.
Set different foregroundColor and mask for those layers. The mask of the left layer will be the left part of the view, and the mask of the right layer will be the right part.
Animate foregroundColor for those layers (simultaneously).
The code of a custom view:
class CustomTextLabel: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .green
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private var textLayer1: CATextLayer?
private var textLayer2: CATextLayer?
func setText(_ text: String, fontSize: CGFloat) {
// create 2 layers with the same text and size, we'll set the colors for them later
textLayer1 = createTextLayer(text, fontSize: fontSize)
textLayer2 = createTextLayer(text, fontSize: fontSize)
// estimate the frame size needed for the text layer with such text and font size
let textSize = textLayer1!.preferredFrameSize()
let w = frame.width, h = frame.height
// calculate the frame such that both layers will be in center of view
let centeredTextFrame = CGRect(x: (w-textSize.width)/2, y: (h-textSize.height)/2, width: textSize.width, height: textSize.height)
textLayer1!.frame = centeredTextFrame
textLayer2!.frame = centeredTextFrame
// set up default color for the text
textLayer1!.foregroundColor = UIColor.yellow.cgColor
textLayer2!.foregroundColor = UIColor.yellow.cgColor
// set background transparent, that's very important
textLayer1!.backgroundColor = UIColor.clear.cgColor
textLayer2!.backgroundColor = UIColor.clear.cgColor
// set up masks, such that each layer's text is visible only in its part
textLayer1!.mask = createMaskLayer(CGRect(x: 0, y: 0, width: textSize.width/2, height: textSize.height))
textLayer2!.mask = createMaskLayer(CGRect(x: textSize.width/2, y: 0, width: textSize.width/2, height: textSize.height))
layer.addSublayer(textLayer1!)
layer.addSublayer(textLayer2!)
}
private var finishColor1: UIColor = .black, finishColor2: UIColor = .black
func animateText(leftPartColor1: UIColor, leftPartColor2: UIColor, rightPartColor1: UIColor, rightPartColor2: UIColor) {
finishColor1 = leftPartColor2
finishColor2 = rightPartColor2
if let layer1 = textLayer1, let layer2 = textLayer2 {
CATransaction.begin()
let animation1 = CABasicAnimation(keyPath: "foregroundColor")
animation1.fromValue = leftPartColor1.cgColor
animation1.toValue = leftPartColor2.cgColor
animation1.duration = 3.0
layer1.add(animation1, forKey: "animation1")
let animation2 = CABasicAnimation(keyPath: "foregroundColor")
animation2.fromValue = rightPartColor1.cgColor
animation2.toValue = rightPartColor2.cgColor
animation2.duration = 3.0
layer2.add(animation2, forKey: "animation2")
CATransaction.setCompletionBlock {
self.textLayer1?.foregroundColor = self.finishColor1.cgColor
self.textLayer2?.foregroundColor = self.finishColor2.cgColor
}
CATransaction.commit()
}
}
private func createTextLayer(_ text: String, fontSize: CGFloat) -> CATextLayer {
let textLayer = CATextLayer()
textLayer.string = text
textLayer.fontSize = fontSize // TODO: also set font name
textLayer.contentsScale = UIScreen.main.scale
return textLayer
}
private func createMaskLayer(_ holeRect: CGRect) -> CAShapeLayer {
let layer = CAShapeLayer()
let path = CGMutablePath()
path.addRect(holeRect)
path.addRect(bounds)
layer.path = path
layer.fillRule = CAShapeLayerFillRule.evenOdd
layer.opacity = 1
return layer
}
}
The calls of a custom view:
class ViewController: UIViewController {
var customLabel: CustomTextLabel!
override func viewDidLoad() {
super.viewDidLoad()
let viewW = view.frame.width, viewH = view.frame.height
let labelW: CGFloat = 200, labelH: CGFloat = 50
customLabel = CustomTextLabel(frame: CGRect(x: (viewW-labelW)/2, y: (viewH-labelH)/2, width: labelW, height: labelH))
customLabel.setText("Optimizing...", fontSize: 20)
view.addSubview(customLabel)
let tapRecogniner = UITapGestureRecognizer(target: self, action: #selector(onTap))
view.addGestureRecognizer(tapRecogniner)
}
#objc func onTap() {
customLabel.animateText(leftPartColor1: UIColor.blue,
leftPartColor2: UIColor.red,
rightPartColor1: UIColor.white,
rightPartColor2: UIColor.black)
}
}
Thanks to Olha's (#OlhaPavliuk) answer, I used two CATextLayer shapes and two CAShapeLayer masks for text layers. In draw method I just change masks frames to calculated size (bounds.width * progress value), and also change the second mask origin to a new start (bounds.width - bounds.width * progress value).
Also, it was very important to set layer.fillRule = CAShapeLayerFillRule.evenOdd while creating a mask, so that both layers became visible.
It turned out that I actually didn't need any animation code involved, because changing frames looks just ok.
In motion: https://giphy.com/gifs/LMbmlMoxY9oaWhXfO1
Full code: https://gist.github.com/joliejuly/a792c2ab8d97d304d731a4a5202f741a

Adding CAShapeLayer to UIButton but it appears under the border line

I want to add a red dot to the border of a UIButton. My current code for adding a dot is like so:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
override func setUp() {
super.setUp()
layer.borderWidth = borderWidth
layer.borderColor = normalBorderColor.cgColor
let redDotLayer = CAShapeLayer()
redDotLayer.path = CGPath(ellipseIn: CGRect(x: 30, y: -3.5, width: 8, height: 8), transform: nil)
redDotLayer.fillColor = UIColor.red.cgColor
layer.addSublayer(redDotLayer)
}
However when I add the red dot it appears under the border line. I need it to be on top of the border line.
What am I doing wrong here?
Nothing wrong with your approach.
For example just consider adding subview to parent UIView, subview always stays inside the parent it can't be added above the parent.
Same scenario applies to CALayer . You can't addSublayer above parent layer.
As an exception,
Unlike views, a superlayer does not automatically clip the contents of sublayers that lie outside its bounds rectangle. Instead, the superlayer allows its sublayers to be displayed in their entirety by default. However, you can reenable clipping by setting the masksToBounds property of the layer to YES.
as per apple documentation sublayer can go beyond parent visible region, but not above the parent.
Even below methods won't help us.
- insertSublayer:atIndex:
- insertSublayer:above:
- insertSublayer:below:
addSublayer:
Appends the layer to the layer’s list of sublayers.
Solution
Draw another CAShapeLayer around the button.
CALayers may just draw the border around itself last. I think the easiest solution for you is just be to draw the border in a separate layer so that you can control the ordering.
override func setUp() {
super.setUp()
let borderLayer = CALayer()
borderLayer.borderWidth = borderWidth
borderLayer.borderColor = normalBorderColor.cgColor
layer.addSublayer(borderLayer)
let redDotLayer = CAShapeLayer()
redDotLayer.path = CGPath(ellipseIn: CGRect(x: 30, y: -3.5, width: 8, height: 8), transform: nil)
redDotLayer.fillColor = UIColor.red.cgColor
layer.addSublayer(redDotLayer)
}
In my code, I was unable to use another layer as a border so I've used another UIView as a border.
An important part is to set the border UIView isUserInteractionEnabled to false so the responder chain would pass it to the UIButton:
let borderView = UIView(frame: .zero)
borderView.backgroundColor = .clear
borderView.layer.borderWidth = 0.5
borderView.layer.borderColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.18).cgColor
borderView.layer.cornerRadius = 19
// Dont't forget to set it to false or else the button won't work
borderView.isUserInteractionEnabled = false

Adding border with width to UIView show small background outside

I'm trying to add circle border to a UIView with green background, I created simple UIView subclass with borderWidth, cornerRadius and borderColor properties and I'm setting it from storyboard.
#IBDesignable
class RoundedView: UIView {
#IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
#IBInspectable var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
}
}
#IBInspectable var borderColor: UIColor {
get {
if let color = layer.borderColor {
return UIColor(cgColor: color)
} else {
return UIColor.clear
}
}
set {
layer.borderColor = newValue.cgColor
}
}
}
But when I compile and run an app or display it in InterfaceBuilder I can see a line outside the border that is still there (and is quite visible on white background).
This RoundedView with green background, frame 10x10, corner radius = 5 is placed in corner of plain UIImageView (indicates if someone is online or not). You can see green border outside on both UIImageView and white background.
Can you please tell me what's wrong?
What you are doing is relying on the layer to draw your border and round the corners. So you are not in charge of the result. You gave it a green background, and now you are seeing the background "stick out" at the edge of the border. And in any case, rounding the corners is a really skanky and unreliable way to make a round view. To make a round view, make a round mask.
So, the way to make your badge is to take complete charge of what it is drawn: you draw a green circle in the center of a white background, and mask it all with a larger circle to make the border.
Here is a Badge view that will do precisely what you're after, with no artifact round the outside:
class Badge : UIView {
class Mask : UIView {
override init(frame:CGRect) {
super.init(frame:frame)
self.isOpaque = false
self.backgroundColor = .clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
let con = UIGraphicsGetCurrentContext()!
con.fillEllipse(in: CGRect(origin:.zero, size:rect.size))
}
}
let innerColor : UIColor
let outerColor : UIColor
let innerRadius : CGFloat
var madeMask = false
init(frame:CGRect, innerColor:UIColor, outerColor:UIColor, innerRadius:CGFloat) {
self.innerColor = innerColor
self.outerColor = outerColor
self.innerRadius = innerRadius
super.init(frame:frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
let con = UIGraphicsGetCurrentContext()!
con.setFillColor(outerColor.cgColor)
con.fill(rect)
con.setFillColor(innerColor.cgColor)
con.fillEllipse(in: CGRect(
x: rect.midX-innerRadius, y: rect.midY-innerRadius,
width: 2*innerRadius, height: 2*innerRadius))
if !self.madeMask {
self.madeMask = true // do only once
self.mask = Mask(frame:CGRect(origin:.zero, size:rect.size))
}
}
}
I tried this with a sample setting as follows:
let v = Badge(frame: CGRect(x:100, y:100, width:16, height:16),
innerColor: .green, outerColor: .white, innerRadius: 5)
self.view.addSubview(v)
It looks fine. Adjust the parameters as desired.
I solved this by using a UIBezierPath and adding to the view's layer:
let strokePath = UIBezierPath(roundedRect: view.bounds, cornerRadius: view.frame.width / 2)
let stroke = CAShapeLayer()
stroke.frame = bounds
stroke.path = strokePath.cgPath
stroke.fillColor = .green.cgColor
stroke.lineWidth = 1.0
stroke.strokeColor = .white.cgColor
view.layer.insertSublayer(stroke, at: 2)
I solved this problem with gradients.
Just seting the backgroundColor of your circle as gradient.
let gradientLayer = CAGradientLayer()
//define colors
gradientLayer.colors = [<<your_bgc_color>>>>, <<border__bgc__color>>]
//define locations of colors as NSNumbers in range from 0.0 to 1.0
gradientLayer.locations = [0.0, 0.7]
//define frame
gradientLayer.frame = self.classView.bounds
self.classView.layer.insertSublayer(gradientLayer, at: 0)
MyImage
An easier fix might be to just mask it like this:
let mask = UIView()
mask.backgroundColor = .black
mask.frame = yourCircleView.bounds.inset(by: UIEdgeInsets(top: 0.1, left: 0.1, bottom: 0.1, right: 0.1))
mask.layer.cornerRadius = mask.height * 0.5
yourCircleView.mask = mask

Reverse layer mask for label

How do you reverse the mask layer for a label? I have a textLabel, which I use as a mask for an imageView that contains an arbitrary image as follows:
let image = UIImage(named: "someImage")
let imageView = UIImageView(image: image!)
let textLabel = UILabel()
textLabel.frame = imageView.bounds
textLabel.text = "Some text"
imageView.layer.mask = textLabel.layer
imageView.layer.masksToBounds = true
The above makes the text in textLabel have a font colour of the imageView as in How to mask the layer of a view by the content of another view?.
How do I reverse this so as to remove the text in textLabel from the imageView?
Make a subclass of UILabel:
class InvertedMaskLabel: UILabel {
override func drawTextInRect(rect: CGRect) {
guard let gc = UIGraphicsGetCurrentContext() else { return }
CGContextSaveGState(gc)
UIColor.whiteColor().setFill()
UIRectFill(rect)
CGContextSetBlendMode(gc, .Clear)
super.drawTextInRect(rect)
CGContextRestoreGState(gc)
}
}
This subclass fills its bounds with an opaque color (white in this example, but only the alpha channel matters). Then it draws the text using the Clear blend mode, which simply sets all channels of the context back to 0, including the alpha channel.
Playground demo:
let root = UIView(frame: CGRectMake(0, 0, 400, 400))
root.backgroundColor = .blueColor()
XCPlaygroundPage.currentPage.liveView = root
let image = UIImage(named: "Kaz-256.jpg")
let imageView = UIImageView(image: image)
root.addSubview(imageView)
let label = InvertedMaskLabel()
label.text = "Label"
label.frame = imageView.bounds
label.font = .systemFontOfSize(40)
imageView.maskView = label
Result:
Since I needed to implement this recently and the syntax has changed a bit, here's a Swift 4.x Version of #RobMayoff's excellent answer. Demo/GitHub repo with Swift Playground located here.
(If you do upvote this, please also upvote his original answer as well :) )
A playground demonstrating the technique. The method drawRect inside InvertedMaskLabel has the secret sauce.
import UIKit
import PlaygroundSupport
// As per https://stackoverflow.com/questions/36758946/reverse-layer-mask-for-label
class InvertedMaskLabel: UILabel {
override func drawText(in rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
context.saveGState()
UIColor.white.setFill()
UIRectFill(rect) // fill bounds w/opaque color
context.setBlendMode(.clear)
super.drawText(in: rect) // draw text using clear blend mode, ie: set *all* channels to 0
context.restoreGState()
}
}
class TestView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .green
let image = UIImage(named: "tr")
let imageView = UIImageView(image: image)
imageview.frame = bounds
addSubview(imageView)
let label = InvertedMaskLabel()
label.text = "Teddy"
label.frame = imageView.bounds
label.font = UIFont.systemFont(ofSize: 30)
imageView.mask = label
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let testView = TestView(frame: CGRect(x: 0, y: 0, width: 400, height: 500))
PlaygroundPage.current.liveView = testView

Override UILabel draw rect inside UIButton

I'm trying to override the UILabel "drawTextInRect" inside a UIButton, so I can create a border around the text.
I have my code that works perfectly with a UILabel
class LabelBorder: UILabel {
override func drawTextInRect(rect: CGRect) {
let shadowOffset = self.shadowOffset
let textColor = self.textColor
let c = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(c, 1)
CGContextSetLineJoin(c, kCGLineJoinRound)
CGContextSetTextDrawingMode(c, kCGTextStroke)
self.textColor = UIColor.blackColor()
self.alpha = 0.35
super.drawTextInRect(rect)
CGContextSetTextDrawingMode(c, kCGTextFill)
self.textColor = textColor
self.shadowOffset = CGSizeMake(0, 0)
self.alpha = 1.0
super.drawTextInRect(rect)
self.shadowOffset = shadowOffset
}
}
But I change a UILabel to a UIButton so I can detect easily the touch up inside action, but I still need the border around the text so I try to modify the code to this
class LabelBorderBtn: UIButton {
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
super.drawRect(rect)
let shadowOffset = self.titleLabel?.shadowOffset
let textColor = self.titleLabel?.textColor
let c = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(c, 1)
CGContextSetLineJoin(c, kCGLineJoinRound)
CGContextSetTextDrawingMode(c, kCGTextStroke)
self.titleLabel?.textColor = UIColor.blackColor()
self.titleLabel?.alpha = 0.35
self.titleLabel?.drawTextInRect(rect)
CGContextSetTextDrawingMode(c, kCGTextFill)
self.titleLabel?.textColor = textColor
self.titleLabel?.shadowOffset = CGSizeMake(0, 0)
self.titleLabel?.alpha = 1.0
self.titleLabel?.drawTextInRect(rect)
self.titleLabel?.shadowOffset = shadowOffset!
}
}
This code didn't work and of course because the TitleLabel its drawing tree times, the default plus two in this code. But there is a way to subclass the UILabel inside the UIButton or any other idea to get border in that label?
I really don't want to detect tap gestures because this buttons are in a tableview with 8 different types of cells
Thanks
If you want your button to have a border you can try this
var button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
button.backgroundColor = UIColor.grayColor()
button.setTitle("Button", forState: UIControlState.Normal)
button.titleLabel?.layer.borderWidth = 1
button.titleLabel?.layer.borderColor = UIColor.redColor().CGColor
I finally solved it, bases on the class I write LabelBorder that I post on the question. The solution is
class LabelBorderBtn: UIButton {
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
super.drawRect(rect)
if let oldLabel = self.titleLabel {
let newLabel = LabelBorder(frame: oldLabel.frame)
newLabel.text = oldLabel.text
newLabel.font = oldLabel.font
newLabel.textColor = oldLabel.textColor
oldLabel.removeFromSuperview()
self.addSubview(newLabel)
}
}
I know it's not a fancy solution, but it works and also works with a UITableView

Resources