UIButton 图片和标题布局扩展

/// 按钮图片和标题的布局样式
/// - Cases:
///   - imageLeft: 图片在标题左侧,可设置标题左侧间距
///   - imageRight: 图片在标题右侧,可设置标题右侧间距
///   - imageTop: 图片在标题上方,可设置两者间距
///   - imageBottom: 图片在标题下方,可设置两者间距
///   - default: 默认布局(图片在左,无间距)
enum ButtonImageTitlePosition {
    case imageLeft(padding: CGFloat = 0)
    case imageRight(padding: CGFloat = 0)
    case imageTop(padding: CGFloat)
    case imageBottom(padding: CGFloat)
    case `default`
}
extension UIButton {
    /// 设置按钮图片和标题的布局样式
    /// - Parameter position: 布局样式及间距配置
    func setImageTitlePosition(_ position: ButtonImageTitlePosition) {
        if #available(iOS 15.0, *) {
            configureForiOS15AndAbove(position)
        } else {
            configureForLegacyiOS(position)
        }
    }
    
    // MARK: - iOS 15+ 配置方法
    @available(iOS 15.0, *)
    private func configureForiOS15AndAbove(_ position: ButtonImageTitlePosition) {
        // 获取或创建按钮配置(保留现有配置)
        var configuration = self.configuration ?? .plain()
        
        // 保持原有的字体样式
        if let titleLabel = self.titleLabel {
            let font = titleLabel.font
            titleLabel.adjustsFontForContentSizeCategory = false // 防止字体自动调整
            configuration.attributedTitle = AttributedString(titleLabel.text ?? "", attributes: AttributeContainer([.font: font as Any]))
            
            // 确保字体不会被 UIButton.Configuration 修改
            configuration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in
                var outgoing = incoming
                outgoing.font = font
                return outgoing
            }
        }
        
        switch position {
        case .imageLeft(let padding):
            configuration.imagePlacement = .leading  // 图片在前(左)
            configuration.imagePadding = padding      // 图片与标题间距
        case .imageRight(let padding):
            configuration.imagePlacement = .trailing // 图片在后(右)
            configuration.imagePadding = padding
        case .imageTop(let padding):
            configuration.imagePlacement = .top      // 图片在上
            configuration.imagePadding = padding
        case .imageBottom(let padding):
            configuration.imagePlacement = .bottom   // 图片在下
            configuration.imagePadding = padding
        case .default:
            configuration.imagePlacement = .leading  // 默认左对齐
            configuration.imagePadding = 0           // 无间距
        }
        
        self.configuration = configuration
    }
    
    // MARK: - iOS 14 及以下版本配置方法
    private func configureForLegacyiOS(_ position: ButtonImageTitlePosition) {
        // 确保 imageView 和 titleLabel 存在
        guard let imageView = self.imageView, let titleLabel = self.titleLabel else { return }
        
        // 获取图片和标题的尺寸
        let imageSize = imageView.frame.size
        let titleSize = titleLabel.frame.size
        let buttonSize = self.bounds.size
        
        /// 计算水平方向上的居中偏移量
        func centerXOffset(for width: CGFloat) -> CGFloat {
            return (buttonSize.width - width) / 2
        }
        
        switch position {
        case .imageLeft(let padding):
            setEdgeInsets(imageLeft: -padding / 2, titleLeft: padding / 2)
        case .imageRight(let padding):
            setEdgeInsets(imageLeft: titleSize.width + padding / 2, titleLeft: -(imageSize.width + padding / 2))
        case .imageTop(let padding):
            let totalHeight = imageSize.height + padding + titleSize.height
            setEdgeInsets(imageTop: -(buttonSize.height - totalHeight) / 2, titleTop: (buttonSize.height - totalHeight) / 2 + imageSize.height + padding)
        case .imageBottom(let padding):
            let totalHeight = imageSize.height + padding + titleSize.height
            setEdgeInsets(imageTop: (buttonSize.height - totalHeight) / 2 + titleSize.height + padding, titleTop: -(buttonSize.height - totalHeight) / 2)
        case .default:
            setEdgeInsets()
        }
    }
    
    /// 设置按钮的内边距(imageEdgeInsets 和 titleEdgeInsets)
    /// - Parameters:
    ///   - imageLeft: 图片左边距调整
    ///   - titleLeft: 标题左边距调整
    ///   - imageTop: 图片上边距调整
    ///   - titleTop: 标题上边距调整
    private func setEdgeInsets(imageLeft: CGFloat = 0, titleLeft: CGFloat = 0, imageTop: CGFloat = 0, titleTop: CGFloat = 0) {
        self.imageEdgeInsets = UIEdgeInsets(top: imageTop, left: imageLeft, bottom: -imageTop, right: -imageLeft)
        self.titleEdgeInsets = UIEdgeInsets(top: titleTop, left: titleLeft, bottom: -titleTop, right: -titleLeft)
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容