Swift基础

【iOS】MaskBtn图片蒙板按钮(swift)

2022-01-12  本文已影响0人  Qire_er

在iOS开发中,用UIImage作为mask蒙板,实现一些有意思的效果,可以说很常见!本文记录我自己最近的项目中,使用UIImage反转后作为mask蒙板,结合UIBlurEffect毛玻璃特效,制作的按钮效果,给有类似需求的朋友作为参考!…… 请看下面的最终效果:


最终效果

如果你对这个感兴趣,那么接下来,我们就重新来做一遍!

制作思路:

  1. 使用CGBlendMode.destinationOut混合模式(反转图片内容)
  2. 将UIImage作为图层的mask蒙板
  3. 添加UIBlurEffect毛玻璃特效作为打底

具体思路如下图:


图片反转Mask

设置图层mask蒙版,直接设置UIview的mask属性就可以了:

 let bgLayer = UIView(frame: CGRect(origin: center, size: size))
 bgLayer.mask = mask // 设置mask属性

图片反转的方法,可以写成一个UIImage的扩展,方便调用,代码如下:

// 反转图片方法
func invert(size: CGSize, color: UIColor, padding: CGFloat, ratio: CGFloat) -> UIImage {
    let blendMode = CGBlendMode.destinationOut // 设置混合模式
    let drawRect = CGRect(origin: .zero, size: size) // 绘制区域
    
    // 定义图片上下文
    UIGraphicsBeginImageContextWithOptions(size, false, scale)
    /*======================================================*/
    color.setFill() // 填充颜色
    UIRectFill(drawRect) // 用指定颜色填充
    draw(in: drawRect.inset(by: UIEdgeInsets(top: padding, left: padding * ratio, bottom: padding, right: padding * ratio)), blendMode: blendMode, alpha: 1.0)
    let invertImage = UIGraphicsGetImageFromCurrentImageContext()!
    /*======================================================*/
    UIGraphicsEndImageContext() // 上下文结束
    
    return invertImage
}

为了使用方便,我是自定义了一个UIButton的子类,考虑到icon图标的长宽比例不同,所以暴露一个ratio比例和padding内边距的参数给外面进行设置。

背景图就随便,这里我用的是一个UIStackView里面放一些色块,主要是跟按钮有个对比,下面是完整代码:

  1. MaskBtn.swift (定义部分的代码)
//
//  MaskBtn.swift
//  UIKit-basic
//
//  Created by Qire_er on 2022/1/12.
//

import UIKit

class MaskBtn: UIButton {
    
    private var icon: UIImage! // icon图片
    private var size: CGSize! // icon尺寸
    private var padding: CGFloat! // icon内边距
    private var ratio: CGFloat! // icon宽高比例
    private var radius: CGFloat! // 圆角半径
    
    init(icon: UIImage, size: CGSize, padding: CGFloat, ratio: CGFloat, radius: CGFloat, tintColor: UIColor) {
        super.init(frame: .zero)
        self.icon = icon
        self.size = size
        self.padding = padding
        self.ratio = ratio
        self.radius = radius
        self.tintColor = tintColor
        createCompose()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // 创建图片蒙板效果
    private func createCompose() {
        let maskImg = icon.invert(size: CGSize(width: size.width, height: size.height), color: tintColor, padding: padding, ratio: ratio)
        let mask = UIImageView(image: maskImg)

        let bgLayer = UIView(frame: CGRect(origin: center, size: size))
        let blur = bgLayer.createBlurEffect(frame: bgLayer.frame)
        
        bgLayer.backgroundColor = tintColor
        bgLayer.mask = mask
        bgLayer.layer.cornerRadius = radius
        bgLayer.isUserInteractionEnabled = false    // 阻止响应用户交互,否则按钮事件无效
        blur.isUserInteractionEnabled = false   // 阻止响应用户交互,否则按钮事件无效
        blur.layer.cornerRadius = radius
        blur.clipsToBounds = true
        
        addSubview(blur)
        addSubview(bgLayer)
    }
}

///////////////////////////////////////////////////////////////////////////////////////////
// 偷懒一下,相关扩展就直接写到这里了!

extension UIImage {
    // 反转图片方法
    func invert(size: CGSize, color: UIColor, padding: CGFloat, ratio: CGFloat) -> UIImage {
        let blendMode = CGBlendMode.destinationOut // 设置混合模式
        let drawRect = CGRect(origin: .zero, size: size) // 绘制区域
        
        // 定义图片上下文
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        /*======================================================*/
        color.setFill() // 填充颜色
        UIRectFill(drawRect) // 用指定颜色填充
        draw(in: drawRect.inset(by: UIEdgeInsets(top: padding, left: padding * ratio, bottom: padding, right: padding * ratio)), blendMode: blendMode, alpha: 1.0)
        let invertImage = UIGraphicsGetImageFromCurrentImageContext()!
        /*======================================================*/
        UIGraphicsEndImageContext() // 上下文结束
        
        return invertImage
    }
}

///////////////////////////////////////////////////////////////////////////////////////////

extension UIView {
    // 创建【毛玻璃】效果
    func createBlurEffect(frame: CGRect) -> UIVisualEffectView {
        let blurEffect = UIBlurEffect(style: .regular)
        let blurView = UIVisualEffectView(effect: blurEffect)
        blurView.frame = frame
        return blurView
    }
}

///////////////////////////////////////////////////////////////////////////////////////////
  1. MaskBtnVC.swift(调用部分的代码)
//
//  MaskBtnVC.swift
//  UIKit-basic
//
//  Created by Qire_er on 2022/1/12.
//

import UIKit

class MaskBtnVC: UIViewController {
    
    // 图片相关配置信息
    // 这里直接用系统图标名称作为key,用ratio比例作为value
    // 另外,size不同,ratio也不一样,暂时没有想出什么更方便的方法!
    private let imgNames = ["heart.fill": 0.75, "camera.fill": 0.65, "book.fill": 0.75, "gearshape.fill": 0.95, "alarm.fill": 1.0, "gift.fill": 0.75]

    override func viewDidLoad() {
        super.viewDidLoad()
        makeBG() // 创建背景图
        makeBtns() // 创建按钮
        view.backgroundColor = .white
    }
    
    // 创建按钮方法
    private func makeBtns() {
        let btnStack = UIStackView()
        btnStack.translatesAutoresizingMaskIntoConstraints = false
        btnStack.axis = .vertical
        btnStack.alignment = .center
        btnStack.distribution = .fillEqually
        
        for item in imgNames {
            let btn = MaskBtn(
                icon: UIImage(systemName: item.key)!,
                size: CGSize(width: 100, height: 100),
                padding: 20,
                ratio: CGFloat(item.value),
                radius: 32,
                tintColor: .white.withAlphaComponent(0.85)
            )
            // 加一点阴影效果
            btn.layer.shadowColor = UIColor.black.cgColor
            btn.layer.shadowOpacity = 0.15
            btn.layer.shadowOffset = CGSize(width: 5, height: 5)
            btn.layer.shadowRadius = 10
            
            btnStack.addArrangedSubview(btn)
            
            btn.widthAnchor.constraint(equalToConstant: 100).isActive = true
        }
        view.addSubview(btnStack)
        
        // 添加约束
        NSLayoutConstraint.activate([
            btnStack.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 15),
            btnStack.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -15),
            btnStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15),
            btnStack.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -5),
        ])
        
    }
    
    // 创建背景图方法
    private func makeBG() {
        let colorStack = UIStackView()
        colorStack.translatesAutoresizingMaskIntoConstraints = false
        colorStack.axis = .vertical
        colorStack.distribution = .fillEqually
        
        // 创建色块
        for i in 0..<30 {
            let colorBlock = UIView()
            colorBlock.backgroundColor = UIColor(
                hue: CGFloat(i * 5 + 16) / 100.0,
                saturation: CGFloat((i * 15) % 100) / 100.0,
                brightness: 0.85,
                alpha: 1.0
            )
            colorStack.addArrangedSubview(colorBlock)
        }
        view.addSubview(colorStack)
        
        // 添加约束
        NSLayoutConstraint.activate([
            colorStack.leftAnchor.constraint(equalTo: view.leftAnchor),
            colorStack.rightAnchor.constraint(equalTo: view.rightAnchor),
            colorStack.topAnchor.constraint(equalTo: view.topAnchor),
            colorStack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])
    }

}

(==完==)

ps: 以上仅代表个人浅见,如果你有什么高见,也欢迎讨论交流!-

上一篇下一篇

猜你喜欢

热点阅读