You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Swift中如何绘制角标并添加至通知图标

实现带动态红色角标的通知图标

核心思路

  • 基于原通知图标,动态绘制红色角标(支持纯红点或带数字样式)
  • 根据新通知状态,切换显示纯图标或带角标的合成图标
  • 将最终生成的图标资源传入自定义组件

Android 端实现示例

1. 编写角标合成工具类

object BadgeUtils {
    fun addBadgeToIcon(context: Context, originalIconResId: Int, hasNewNotification: Boolean): Bitmap {
        val originalBitmap = BitmapFactory.decodeResource(context.resources, originalIconResId)
        // 创建可编辑的Bitmap副本
        val editableBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true)
        val canvas = Canvas(editableBitmap)
        val paint = Paint().apply {
            color = Color.RED
            isAntiAlias = true
            style = Paint.Style.FILL
        }

        if (hasNewNotification) {
            // 绘制右上角红色圆形角标,尺寸占图标宽度的20%
            val badgeRadius = originalBitmap.width * 0.2f
            val badgeX = originalBitmap.width - badgeRadius
            val badgeY = badgeRadius
            canvas.drawCircle(badgeX, badgeY, badgeRadius, paint)

            // 如需显示未读数字,取消注释以下代码
            // val textPaint = Paint().apply {
            //     color = Color.WHITE
            //     textSize = badgeRadius * 1.2f
            //     textAlign = Paint.Align.CENTER
            //     isAntiAlias = true
            // }
            // canvas.drawText("5", badgeX, badgeY + badgeRadius * 0.4f, textPaint)
        }
        // 释放原Bitmap资源
        originalBitmap.recycle()
        return editableBitmap
    }
}

2. 传入自定义组件

// 检查是否有新通知(自行实现业务逻辑)
val hasNewNotification = checkUnreadNotifications()
// 合成带角标的图标
val badgeIcon = BadgeUtils.addBadgeToIcon(this, R.drawable.ic_notification, hasNewNotification)
// 传给自定义组件
customImageComponent.setIcon(badgeIcon)

iOS 端实现示例

1. 编写角标合成工具类

class BadgeHelper {
    static func addBadgeToIcon(originalImage: UIImage, hasNewNotification: Bool) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(originalImage.size, false, UIScreen.main.scale)
        originalImage.draw(in: CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.height))
        
        if hasNewNotification {
            // 绘制右上角红色圆形角标
            let badgeRadius = originalImage.size.width * 0.2
            let badgeFrame = CGRect(
                x: originalImage.size.width - badgeRadius,
                y: 0,
                width: badgeRadius * 2,
                height: badgeRadius * 2
            )
            UIColor.red.setFill()
            UIBezierPath(ovalIn: badgeFrame).fill()

            // 如需显示未读数字,取消注释以下代码
            // let text = "5"
            // let textAttributes: [NSAttributedString.Key: Any] = [
            //     .font: UIFont.systemFont(ofSize: badgeRadius * 1.2),
            //     .foregroundColor: UIColor.white
            // ]
            // let textSize = text.size(withAttributes: textAttributes)
            // let textRect = CGRect(
            //     x: originalImage.size.width - badgeRadius - textSize.width/2,
            //     y: badgeRadius - textSize.height/2,
            //     width: textSize.width,
            //     height: textSize.height
            // )
            // text.draw(in: textRect, withAttributes: textAttributes)
        }
        
        let finalImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return finalImage
    }
}

2. 传入自定义组件

// 检查是否有新通知(自行实现业务逻辑)
let hasNewNotification = checkUnreadNotifications()
if let originalImage = UIImage(named: "ic_notification") {
    let badgeImage = BadgeHelper.addBadgeToIcon(originalImage: originalImage, hasNewNotification: hasNewNotification)
    // 传给自定义组件
    customImageView.image = badgeImage
}

Flutter 端实现示例(无需合成图片)

直接通过Stack组件叠加实现状态切换,更灵活高效:

Widget buildNotificationIcon(bool hasNewNotification) {
  return Stack(
    alignment: Alignment.topRight,
    children: [
      // 原通知图标
      Image.asset("assets/ic_notification.png", width: 40, height: 40),
      // 有新通知时显示角标
      if (hasNewNotification)
        Container(
          width: 16,
          height: 16,
          decoration: const BoxDecoration(
            color: Colors.red,
            shape: BoxShape.circle,
          ),
          // 如需显示未读数字,取消注释以下代码
          // child: const Text(
          //   "5",
          //   style: TextStyle(color: Colors.white, fontSize: 12),
          //   textAlign: TextAlign.center,
          // ),
        ),
    ],
  );
}

传入自定义组件:

// 检查是否有新通知(自行实现业务逻辑)
bool hasNewNotification = checkUnreadNotifications();
// 传给自定义组件
CustomImageComponent(
  icon: buildNotificationIcon(hasNewNotification),
)

注意事项

  • 角标的大小、位置可根据图标实际尺寸调整,保证视觉协调
  • Android 端处理 Bitmap 时注意资源释放,避免内存泄漏
  • 如果需要支持角标数字超过9时显示"9+",可在文本绘制逻辑中添加判断

内容的提问来源于stack exchange,提问作者Jayaseelan Kumarasamy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 11:25:29