【iOS】MaskBtn图片蒙板按钮(swift)
2022-01-12 本文已影响0人
Qire_er
在iOS开发中,用UIImage作为mask蒙板,实现一些有意思的效果,可以说很常见!本文记录我自己最近的项目中,使用UIImage反转后作为mask蒙板,结合UIBlurEffect毛玻璃特效,制作的按钮效果,给有类似需求的朋友作为参考!…… 请看下面的最终效果:
最终效果
如果你对这个感兴趣,那么接下来,我们就重新来做一遍!
制作思路:
- 使用CGBlendMode.destinationOut混合模式(反转图片内容)
- 将UIImage作为图层的mask蒙板
- 添加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里面放一些色块,主要是跟按钮有个对比,下面是完整代码:
- 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
}
}
///////////////////////////////////////////////////////////////////////////////////////////
- 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: 以上仅代表个人浅见,如果你有什么高见,也欢迎讨论交流!-