Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use table view disclosure indicator style for uibutton ios

Tags:

ios

uibutton

I have a custom view and i want to imitate the disclosure indicator found in table view cells. Is this possible ? Is there anyway of extracting that image ?

like image 228
VBK Avatar asked Dec 12 '12 09:12

VBK


8 Answers

This can be done entirely with code by placing a UITableViewCell with the disclosure indicator within a UIButton:

UITableViewCell *disclosure = [[UITableViewCell alloc] init];
disclosure.frame = button.bounds;
disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
disclosure.userInteractionEnabled = NO; 

[button addSubview:disclosure];

Swift:

let disclosure = UITableViewCell()
disclosure.frame = button.bounds
disclosure.accessoryType = .disclosureIndicator
disclosure.isUserInteractionEnabled = false

button.addSubview(disclosure)
like image 176
Avario Avatar answered Oct 10 '22 04:10

Avario


Since Apple provides official iOS design resources for different tools you can extract the chevron from there.


Update

Apple announced the icon font SF Symbols on the WWDC'19 keynote.

The SF Symbols companion app contains an icon called chevron.right ready for you to use. You can also Specify the thickness of the icon.

enter image description here

like image 23
heyfrank Avatar answered Oct 10 '22 04:10

heyfrank


Note that you must use a transparent background

Here's the best possible match I can get, in a photoshop file:

Note that it includes the REAL iOS IMAGE (from a screenshot) underneath as a layer, so you can compare.

http://www.filedropper.com/fakearrowiosnov2013psd


It seems like VBK wants the single chevron from the UITableView collection. Which is called 'Disclosure Indicator' as opposed to the one available from UIButton which is 'Detail Disclosure'.

I think you want something like this:

UITableView Disclosure Indicator Image for Buttons

It is 50x80 with a transparent background. Use this image on top of a button or UIImageView. Resize it to whatever size you'd like your button to be. Apple recommends a hit target of no less than 40x40. I sized it to 10x16 in my storyboard, but I am using a transparent button overlay so the size doesn't matter.

Image Mirror : http://imgur.com/X00qn0Z.png


Just note however this is not precisely the image used in iOS7. (Nov 2013.) To get the exact image, simply run an app in retina in your simulator, and make a screenshot.

like image 31
mevdev Avatar answered Oct 10 '22 05:10

mevdev


What I like to do is to draw it using UIBezierPath. This gives me a freedom to resize if I want to, without loosing clarity. And it also gives me an opportunity to change the colour later on if that's what I need without a photo editor.The principle is generic and applicable to any given path. The usage is quite simple:

//suppose we want to apply disclosure arrow image to this button:
@IBOutlet weak var btnDisclosure: UIButton!

All I need to do now is:

//get an image from UIBezierPath, resize it for the button and stroke with white:
let arrowImage = UIImage.imageWithBezierPath(UIBezierPath.disclosureArrowPath().scaleToAspectFitRect(CGRect(x: 0, y: 0, width: 22, height: 22)), fillColor: UIColor.clearColor(), strokeColor: UIColor.whiteColor())

//assign disclosure arrow image to the button:
btnDisclosure.setImage(arrowImage, forState: .Normal)

So, a piece of code for drawing the UIBezierPath which looks like disclosure button:

extension UIBezierPath
{
    ///Disclosure arrow path. Use scaleToAspectFitRect to resize it to any given rect.
    class func disclosureArrowPath() -> UIBezierPath
    {
        //// arrow Drawing
        let arrowPath = UIBezierPath()
        arrowPath.moveToPoint(CGPointMake(4, 4))
        arrowPath.addLineToPoint(CGPointMake(26.5, 25.24))
        arrowPath.addLineToPoint(CGPointMake(4, 47.5))
        arrowPath.lineWidth = 3

        return arrowPath
    }  

    ///Makes a path scalable to any size.
    ///- parameter newRect: The path will be resized to aspect fit into this rectangle.
    func scaleToAspectFitRect(newRect: CGRect) -> UIBezierPath
    {
        var scaleFactor : CGFloat = 1.0

        //this is probably only the case of scale factor < 1:
        if bounds.width > bounds.height
        {
            //fit witdth:
            scaleFactor = newRect.width/bounds.width
        }
        else
        {
            //fit height:
            scaleFactor = newRect.height/bounds.height
        }

        //scale to aspect fill rect:
        self.applyTransform(CGAffineTransformMakeScale(scaleFactor, scaleFactor))

        return self
    }
}

Next, you need a way to get the UIImage out of UIBezierPath. Again, you could add extension to UIImage that will do this like so:

extension UIImage
{
    ///Custom fill and stroke colours for our image based on UIBezierPath
    class func imageWithBezierPath(path: UIBezierPath, fillColor: UIColor, strokeColor: UIColor) -> UIImage
    {
        //enlarge the rect so that stroke line is not clipped:
        let rect = CGRectInset(path.bounds, -path.lineWidth / 2, -path.lineWidth / 2)

        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0) //size of the image, opaque, and scale (set to screen default with 0)

        let bezierLayer = CAShapeLayer()
        bezierLayer.path = path.CGPath;
        bezierLayer.fillColor = fillColor.CGColor
        bezierLayer.strokeColor = strokeColor.CGColor
        bezierLayer.lineWidth = path.lineWidth;

        let imgViewTmp = UIImageView(frame: path.bounds)
        imgViewTmp.layer.addSublayer(bezierLayer);
        imgViewTmp.layer.renderInContext(UIGraphicsGetCurrentContext()!)

        let image = UIGraphicsGetImageFromCurrentImageContext()

        //UIGraphicsEndImageContext()
        return image
    }
}

It may seem as an overkill for this specific task, but it's generic. And it's really handy if you deal a lot with resizing, trying to figure out the right design etc.

like image 35
Despotovic Avatar answered Oct 10 '22 05:10

Despotovic


I made an entirely in code solution to draw arrows similar to UITableView disclosure indicator.

It's used like this:

let arrowImage = ArrowImageGenerator.generateArrow(withDirection: .down)

The default arrow looks the same as the default for the UITableView disclosure indicator. If you want you can customize direction (up, down, left, right), size, color and so on.

Here is the code:

//
//  ArrowImageGenerator.swift
//
//  Created by Alessio Orlando on 07/10/15.
//  Copyright © 2015 Alessio Orlando. All rights reserved.
//

import Foundation
import UIKit

enum ArrowDirection {
    case up
    case down
    case left
    case right
}

class ArrowImageGenerator {

    static var defaultColor: UIColor = {
        let color = UIColor(red: 0.783922, green: 0.780392, blue: 0.8, alpha: 1)
        return color
    }()

    class func generateArrow(withDirection direction: ArrowDirection = .right,
                             size: CGSize? = nil,
                             lineWidth: CGFloat = 2.0,
                             arrowColor: UIColor = ArrowImageGenerator.defaultColor,
                             backgroundColor: UIColor = UIColor.clear,
                             scale: CGFloat = UIScreen.main.scale)
        -> UIImage? {

            var actualSize: CGSize
            if let size = size {
                actualSize = size
            }
            else {
                actualSize = defaultSize(for: direction)
            }

            let scaledSize = actualSize.applying(CGAffineTransform(scaleX: scale, y: scale))
            let scaledLineWidth = lineWidth * scale

            UIGraphicsBeginImageContext(CGSize(width: scaledSize.width, height: scaledSize.height))
            defer {
                UIGraphicsEndImageContext()
            }

            guard let context = UIGraphicsGetCurrentContext() else { return nil }
            configureForArrowDrawing(context)

            UIGraphicsPushContext(context)
            strokeArrow(context, size: scaledSize, arrowColor: arrowColor, backgroundColor: backgroundColor, lineWidth: scaledLineWidth, direction: direction)
            UIGraphicsPopContext()

            guard let outputImage = UIGraphicsGetImageFromCurrentImageContext(),
                let quartzImage = context.makeImage() else {
                return nil
            }

            let scaledImage = UIImage(cgImage: quartzImage, scale: scale, orientation: outputImage.imageOrientation)
            return scaledImage
    }

    private class func generateResizableArrow(_ arrowImage: UIImage, direction: ArrowDirection) -> UIImage {
        var edgeInset: UIEdgeInsets?
        switch direction {
        case .up:
            edgeInset = UIEdgeInsets(top: 11, left: 0, bottom: 1, right: 0)
        case .down:
            edgeInset = UIEdgeInsets(top: 1, left: 0, bottom: 11, right: 0)
        case .left:
            edgeInset = UIEdgeInsets(top: 1, left: 11, bottom: 1, right: 0)
        case .right:
            edgeInset = UIEdgeInsets(top: 1, left: 0, bottom: 1, right: 11)
        }
        let resizableImage = arrowImage.resizableImage(withCapInsets: edgeInset!)
        return resizableImage
    }

    private class func configureForArrowDrawing(_ context: CGContext) {
        context.setBlendMode(CGBlendMode.normal)
        context.setAllowsAntialiasing(true)
        context.setShouldAntialias(true)
    }

    private class func strokeArrow(_ context: CGContext, size: CGSize, arrowColor: UIColor, backgroundColor: UIColor, lineWidth: CGFloat = 1.0, direction: ArrowDirection) {
        backgroundColor.setFill()
        UIRectFill(CGRect(origin: CGPoint(x: 0, y: 0), size: size))
        arrowColor.setStroke()
        context.setLineWidth(lineWidth)
        let lineWidthOffset = lineWidth / 2 // needed to make the arrow pointy.

        switch direction {
        case .up:
            context.move(to: CGPoint(x: size.width, y: size.height))
            context.addLine(to: CGPoint(x: size.width / 2, y: 0 + lineWidthOffset))
            context.addLine(to: CGPoint(x: 0, y: size.height))
        case .down:
            context.move(to: CGPoint(x: size.width, y: 0))
            context.addLine(to: CGPoint(x: size.width / 2, y: size.height - lineWidthOffset))
            context.addLine(to: CGPoint(x: 0, y: 0))
        case .left:
            context.move(to: CGPoint(x: size.width, y: 0))
            context.addLine(to: CGPoint(x: lineWidthOffset, y: size.height / 2))
            context.addLine(to: CGPoint(x: size.width, y: size.height))
        case .right:
            context.move(to: CGPoint(x: 0, y: 0))
            context.addLine(to: CGPoint(x: size.width - lineWidthOffset, y: size.height / 2))
            context.addLine(to: CGPoint(x: 0, y: size.height))
        }
        context.strokePath()
    }

    class func defaultSize(for direction: ArrowDirection) -> CGSize {
        switch direction {
        case .up, .down:
            return CGSize(width: 12, height: 7)
        case .left, .right:
            return CGSize(width: 7, height: 12)
        }
    }
}

Here is the complete gist: github gist

like image 42
ale84 Avatar answered Oct 10 '22 05:10

ale84


You can extract the graphic images from the Xcode Simulator using this Xcode project - https://github.com/0xced/iOS-Artwork-Extractor

like image 24
Trevor Mayhew Avatar answered Oct 10 '22 04:10

Trevor Mayhew


This worked for me:

UITableViewCell *disclosure = [[UITableViewCell alloc] init];
disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
for (UIView*v1 in disclosure.subviews)
{
   if ([v1 isKindOfClass:[UIButton class]])
   {
       for (UIView*v2 in v1.subviews)
       {
           if ([v2 isKindOfClass:[UIImageView class]])
           {
               return ((UIImageView*)v2).image;
           }
       }
   }
}
like image 42
sarge Avatar answered Oct 10 '22 05:10

sarge


SWIFT 5

private lazy var iconImageView: UIImageView = {
    let imageView = UIImageView()
    let configuration = UIImage.SymbolConfiguration(pointSize: 13, weight: .medium)
    imageView.image = UIImage(systemName: "chevron.right", withConfiguration: configuration)
    imageView.tintColor = .lightGray
    imageView.contentMode = .scaleAspectFit
    imageView.constrainAspectRatio(17.0/10.0)
    imageView.translatesAutoresizingMaskIntoConstraints = false
    return imageView
}()

Aspect Ratio UIView Extension

extension UIView {

/// Ratio height/width.  Example: 20/40 (20 is height, 40 is width)
func constrainAspectRatio(_ ratio: CGFloat) {
    NSLayoutConstraint(item: self,
                       attribute: .height,
                       relatedBy: .equal,
                       toItem: self,
                       attribute: .width,
                       multiplier: ratio,
                       constant: 0).isActive = true
    }
}
like image 44
Joshua Hart Avatar answered Oct 10 '22 04:10

Joshua Hart