如何复刻iOS 17按钮点击时的高亮与动画效果(自定义ButtonStyle)
针对固定opacity/brightness无法适配多背景色的问题,这里提供几个贴近苹果原生逻辑的解决方案:
方案1:iOS17+ 直接使用系统原生高亮修饰符(最推荐)
苹果在iOS17中提供了highlightEffect()修饰符,它会自动根据按钮背景色、当前明暗模式生成适配的高亮效果,完全不需要手动调整颜色参数。直接替换原有代码即可:
struct CustomButtonStyle: ButtonStyle { let backgroundColor: Color = .mint func makeBody(configuration: Configuration) -> some View { configuration.label .frame(minWidth: 240) .foregroundStyle(.white) .font(.system(size: 19, design: .rounded).weight(.semibold)) .background(backgroundColor, in: RoundedRectangle(cornerRadius: 10, style: .continuous)) .highlightEffect() // 原生高亮效果,自动适配所有颜色和模式 } }
方案2:基于背景色亮度动态生成遮罩
如果需要兼容iOS17以下版本,可以通过计算背景色的亮度,动态切换高亮遮罩颜色(亮背景用深色半透明遮罩,暗背景用浅色半透明遮罩),完全模拟苹果原生的高亮逻辑:
首先添加计算颜色亮度的扩展:
extension Color { // 基于WCAG标准计算颜色相对亮度 var brightness: Double { let uiColor = UIColor(self) var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0 uiColor.getRed(&red, green: &green, blue: &blue, alpha: nil) return Double(0.2126 * red + 0.7152 * green + 0.0722 * blue) } }
然后修改ButtonStyle:
struct CustomButtonStyle: ButtonStyle { let backgroundColor: Color = .mint func makeBody(configuration: Configuration) -> some View { // 根据背景亮度选择高亮遮罩颜色 let highlightMask = backgroundColor.brightness > 0.5 ? Color.black.opacity(0.2) : Color.white.opacity(0.2) configuration.label .frame(minWidth: 240) .foregroundStyle(.white) .font(.system(size: 19, design: .rounded).weight(.semibold)) .background(backgroundColor, in: RoundedRectangle(cornerRadius: 10, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 10, style: .continuous) .fill(highlightMask) .opacity(configuration.isPressed ? 1 : 0) ) .animation(.easeInOut(duration: 0.1), value: configuration.isPressed) } }
方案3:使用混合模式实现自适应高亮
利用SwiftUI的blendMode(.overlay),可以让高亮层自动根据底层背景色调整明暗——白色叠加在亮色上会变暗,叠加在暗色上会变亮,效果接近原生:
struct CustomButtonStyle: ButtonStyle { let backgroundColor: Color = .mint func makeBody(configuration: Configuration) -> some View { configuration.label .frame(minWidth: 240) .foregroundStyle(.white) .font(.system(size: 19, design: .rounded).weight(.semibold)) .background(backgroundColor, in: RoundedRectangle(cornerRadius: 10, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 10, style: .continuous) .fill(Color.white) .blendMode(.overlay) .opacity(configuration.isPressed ? 0.3 : 0) ) .animation(.easeInOut(duration: 0.1), value: configuration.isPressed) } }
内容的提问来源于stack exchange,提问作者Womble
相关产品推荐
相关产品推荐

