11

I am currently working on a welcome page and I came across this interesting design:

credit to Julian Bialowan for the design: https://dribbble.com/shots/1807682-Everest-Welcome

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?

mfaani
  • 33,269
  • 19
  • 164
  • 293
Kevin Rajan
  • 827
  • 8
  • 28

3 Answers3

29

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
}

enter image description here

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Very useful code and easily cleared all of my doubts. Thanks a lot !! But there is a problem, the text portion is transparent but the background is blue coloured instead of white and alpha component. What have I missed ? – Sushree Swagatika Feb 21 '17 at 13:11
  • @SushreeSwagatika - If you're using this image in a tab bar or the like, the color channel is ignored and it uses whatever tint appropriate. But if you use this in a standard `UIImageView` or `UIButton`, you should see whatever color you used when calling `fill()`. – Rob Mar 03 '18 at 19:54
2

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.

Alan Haden
  • 147
  • 14
  • 1
    You can also use PNG format, which also supports tranparencies. The key is that you just don't want to use JPG. – Rob Apr 23 '16 at 18:24
0

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

Ketan Parmar
  • 27,092
  • 9
  • 50
  • 75