完美解决UIButton imageView大小控制问题,完美适配iOS13系统图标的降级方案

2023-02-15 15:06:24 浏览数 (2)

在进行iOS应用开发的时候,经常会用到带有图标的按钮。

最近在更新账号小助手的时候,我发现xcode更新了一系列的系统图标,而且下拉一看都是十分规范而精美的,涵盖的内容也很丰富,这对于我们这样的独立的开发来说可以说是雪中送炭。

在网上搜一搜,不难发现这些系统图标是源自于2019WWDC大会的发布,并且苹果官方给了一套说明,甚至还有一个方便查找的mac app 传送门: Human Interface Guidelines - SF Symbols

而在具体的开发时,也还是能发现一些问题。

1. 最新的图标用起来固然爽,但是需要自己考虑向下兼容

可以看到,这个组件在iOS12或者更早时是无效的,我们如果在代码中用到相关的接口来获取图片对象时,同样也会告警处理低版本兼容问题。

代码语言:javascript复制
UIImage( systemNamed: "doc.text" ) 

那我们如果为了兼容只能使用图片来替代了。 好在经过查找,我们发现 SF Symbols App是可以将矢量图标导出的,那我们就可以再使用其他工具生成对应的@x 图片文件了。

虽然还是回到了用图解决问题,但好在我们在做一些小部分的系统类Icon时候不用再费力去找素材了.

2. 使用图片降级方案时,按钮中图片大小成为烦恼

我们知道按钮中的图标,一般需要随着按钮的大小而自动调整,而在xcode中,我们将图片资源设置到对应的storyboard 或是 代码中的 imageView, UIButton.setImage 都会出现,图片保持了原图的大小这样的问题,并且还是被拉伸的状态。

在解决这个问题的时候,要解决几个问题 a. 保持图片的缩放比例 b. 图片颜色应该和文字、tintColor一致 c. 图片应该缩放到和正常的Symbol图标一致或接近

a 是比较好解决的,主要使用 contentMode 属性

b 需要同时设置RenderingMode 以及 tintColor

c 就比较麻烦了,最初的思路是设置UIButton下的imageView的size,frame,但是没有任何效果,网上查了很多在这个部分也是毫无收获,后来发现UIButton的图片机制完全是基于 imageEdgeInset 自动计算的。 希望自由控制按钮中图片的同学也可以注意了,使用imageEdgeInset设置图片基于按钮的上下左右距离,剩下来的空间的就是图片的最终size

不过只要思路弄清楚了,解决方案就不是问题。 下面是我的解题思路和方程式:

既然苹果是自动计算的,那我也自己也来自动计算一下图片的大小好了~

代码语言:javascript复制
extension UIButton{

    // 使用SF图标 size期望图标大小 为空自动计算最佳大小
    func setSFIcon( name:String, size:CGFloat? = nil ){
        /**
        设置按钮中的图片图标 (只考虑图标在文字左侧)
        1 拿到button大小
        2 拿到label大小
        3 拿到期望图标大小 / 计算最佳大小 ( 图标大小不能超过按钮 2/3高度, 只考虑正方形图标容器 )
        4 计算间隙 ( 图标默认离文字要有 1/5 的距离 )
         */

        let buttonSize = self.size
        let labelSize  = self.titleLabel?.size ?? CGSize(width: 0, height: 0)
        var bestSize:CGFloat

        if size != nil{
            bestSize   = size! > (buttonSize.height * 2) / 3 ? (buttonSize.height * 2) / 3 : size!
        }else{
            bestSize   = 1.2 * labelSize.height
        }

        let left   = (buttonSize.width - 1.2 * bestSize - labelSize.width) / 2
        let right  = (buttonSize.width - left - (bestSize * 1.2 ) )
        let hInset = (buttonSize.height - bestSize) / 2

        let icon = UIImage.SFIcon(name: name)?.withRenderingMode(.alwaysTemplate)

        self.imageEdgeInsets = UIEdgeInsets(top: hInset, left: left, bottom: hInset, right: right)
        self.imageView?.contentMode = .scaleAspectFit

        self.setImage( icon, for: .normal)
        self.tintColor = self.currentTitleColor
    }
}


extension UIImage{
    // 获取SF图标 早期系统使用图片资源
    static func SFIcon( name:String ) -> UIImage?{

        if #available(iOS 13.0, *) {
            return UIImage( systemName: name )
        } else {
            return UIImage( named: name )
        }
    }
}

而在对应的ViewController中,调用就十分简单了

代码语言:javascript复制
    copyButton.setSFIcon(name: "doc.on.doc", size: 18)

0 人点赞