项目中经常遇到这种分割线隔开的分段按钮视图,将完美的实现方式总结记录一下(可以自定义视图,添加子视图的方式实现,但是那永远不是最好的选择,我试过 n 种);
![](https://img.haomeiwen.com/i281882/217cc8f896f263f8.png)
但是官方默认只支持 UIImage 或者 String,only one!
open func setTitle(_ title: String?, forSegmentAt segment: Int) // can only have image or title, not both. must be 0..#segments - 1 (or ignored). default is nil
open func setImage(_ image: UIImage?, forSegmentAt segment: Int) // can only have image or title, not both. must be 0..#segments - 1 (or ignored). default is nil
所以需要自己思考如何 支持 both? 一种是重写 UISegmentedControl 分段子控件,在子控件视图进行绘制,肯定能实现需求,但是太复杂,思考是否有更加简单的办法?灵光一闪:直接设置 UIImage,在图像中包含所有信息不就 ok 了,随想随动:
第一步:图像绘制图像+文字:
@objc public extension UIImage {
///扩展便利构造器
convenience init(color: UIColor) {
let image = UIImage.color(color)
if let cgImage = image.cgImage {
self.init(cgImage: cgImage)
} else {
self.init(cgImage: UIImage.color(.white).cgImage!)
}
}
/// 把颜色转成UIImage
static func color(_ color: UIColor) -> UIImage{
let size: CGSize = CGSize(width: 1, height: 1)
let rect: CGRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
defer {
UIGraphicsEndImageContext()
}
let context: CGContext = UIGraphicsGetCurrentContext()!
context.setFillColor(color.cgColor)
context.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image ?? UIImage()
}
///生成含有图片和文字的图像
static func textEmbededImage(image: UIImage, string: String, color: UIColor, imageAlignment: Int = 0, segFont: UIFont? = nil) -> UIImage {
let font = segFont ?? UIFont.systemFont(ofSize: 16.0)
let expectedTextSize: CGSize = (string as NSString).size(withAttributes: [NSAttributedString.Key.font: font])
let width: CGFloat = expectedTextSize.width + image.size.width + 5.0
let height: CGFloat = max(expectedTextSize.height, image.size.width)
let size: CGSize = CGSize(width: width, height: height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
let context: CGContext = UIGraphicsGetCurrentContext()!
context.setFillColor(color.cgColor)
let fontTopPosition: CGFloat = (height - expectedTextSize.height) / 2.0
let textOrigin: CGFloat = (imageAlignment == 0) ? image.size.width + 5 : 0
let textPoint: CGPoint = CGPoint(x: textOrigin, y: fontTopPosition)
string.draw(at: textPoint, withAttributes: [NSAttributedString.Key.font: font,
NSAttributedString.Key.foregroundColor: color])
let flipVertical: CGAffineTransform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height)
context.concatenate(flipVertical)
let alignment: CGFloat = (imageAlignment == 0) ? 0.0 : expectedTextSize.width + 5.0
context.draw(image.cgImage!, in: CGRect(x: alignment,
y: ((height - image.size.height) / 2.0),
width: image.size.width,
height: image.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
第二步:设置UISegmentedControl 外观
let clearColorImage = UIImage(color: .clear)
view.setBackgroundImage(clearColorImage, for: .normal, barMetrics: .default)
view.setBackgroundImage(clearColorImage, for: .selected, barMetrics: .default)
view.setBackgroundImage(clearColorImage, for: .highlighted, barMetrics: .default)
view.setDividerImage(UIImage(color: .line), forLeftSegmentState: [.normal, .selected, .highlighted], rightSegmentState: .normal, barMetrics: .default)
第三步:设置UISegmentedControl 图像
//图像必须 withRenderingMode(.alwaysOriginal) 选择原始渲染,不然会被 tintColor 渲染为纯色图片
cell.segmentCtl.setImage(UIImage(named: "icon_find_route")?.withRenderingMode(.alwaysOriginal), forSegmentAt: 0)
cell.segmentCtl.setImage(UIImage(named: "icon_go_here")?.withRenderingMode(.alwaysOriginal), forSegmentAt: 1)
完美实现!!!
网友评论