Avatars are a ubiquitous part of modern UI design, commonly used to represent users or entities. In this post, we’ll dissect a SwiftUI based avatar component that supports customization of size, state, and content type. By the end, you’ll have a clear understanding of its architecture and how to leverage it in your own projects.
Component Overview
This avatar component is built with modularity and flexibility in mind. It includes three main elements:
- AvatarType: Determines the type of content displayed (icon, initials, or an image).
- AvatarSize: Configures predefined sizes for the avatar and its fonts or badges.
- DLAvatarState: Represents the state of the avatar, such as online or offline, and adjusts the badge appearance accordingly.
Breaking Down the Code
1. Defining the Avatar Types
The AvatarType
enum specifies the types of content you can show in the avatar:
public enum AvatarType {
case icon(icon: String)
case initials(text: String)
case image(image: String)
}
icon
: Displays a system or custom icon.initials
: Shows user initials.image
: Uses an image resource for the avatar.
This abstraction allows for versatile content representation.
2. Configuring Sizes with AvatarSize
The AvatarSize
enum encapsulates predefined avatar sizes, making it easy to maintain consistency across the UI:
enum AvatarSize {
case xs, sm, md, lg
var size: CGFloat {
switch self {
case .xs: 32
case .sm: 40
case .md: 48
case .lg: 56
}
}
var fontSize: CGFloat {
switch self {
case .xs: return 12
case .sm: return 14
case .md: return 18
case .lg: return 20
}
}
var badgeSize: CGFloat {
switch self {
case .xs: return 12
case .sm: return 14
case .md: return 16
case .lg: return 18
}
}
var badgeTextSize: CGFloat {
switch self {
case .xs, .sm: return 8
case .md, .lg: return 12
}
}
var offset: CGFloat {
switch self {
case .xs: return 1
default: return 3
}
}
}
Each size has associated properties:
size
: The overall dimension of the avatar.fontSize
: Font size for initials.badgeSize
: Size of the status badge.badgeTextSize
: Text size inside the badge.offset
: Adjusts badge position for better alignment.
This configuration simplifies size management and ensures scalability.
3. Handling States with DLAvatarState
The DLAvatarState
enum adds a layer of interactivity by enabling state-based badges:
public enum DLAvatarState {
case normal, online, offline
var bgColor: Color {
switch self {
case .online: return .green
case .offline: return .green.opacity(0.5)
default: return .clear
}
}
}
bgColor
: Determines the badge background color:.online
: Green..offline
: Faded green..normal
: Clear (no badge).
This ensures that the avatar’s state is visually distinguishable.
4. Building the Badge Component
The DLAvatarBadge
view draws a circular badge to represent the user’s state:
struct DLAvatarBadge: View {
var ckAvatarSizing: AvatarSize
var state: AvatarState
public var body: some View {
Circle()
.fill(state.bgColor)
.frame(width: ckAvatarSizing.badgeSize, height: ckAvatarSizing.badgeSize)
.overlay {
Circle()
.stroke(.white, lineWidth: 2)
}
}
}
The badge size and appearance are dynamically tied to the AvatarSize
and AvatarState
.
5. Putting It All Together in the Avatar View
Finally, the Avatar
view combines all the components:
struct Avatar: View {
var type: AvatarType
var size: AvatarSize
var state: AvatarState
var customSize: CGFloat? = nil
public var body: some View {
ZStack {
switch type {
case .icon(let icon):
Image(icon)
.resizable()
.frame(width: 24, height: 24)
case .initials(let text):
Text(text)
.font(.system(size: size.fontSize, weight: .semibold))
.lineLimit(1)
case .image(let image):
Image(image)
.resizable()
.aspectRatio(contentMode: .fill)
}
}
.frame(width: customSize ?? size.size, height: customSize ?? size.size)
.background(.white)
.clipShape(.circle)
.overlay {
Circle()
.stroke(Color.grey200)
}
.overlay(alignment: .bottomTrailing) {
if state != .normal {
DLAvatarBadge(
ckAvatarSizing: size,
state: state
)
.offset(x: size.offset, y: size.offset)
}
}
}
}
Key Features:
- Content Display: The
type
property determines whether to show an icon, initials, or image. - Styling: The avatar is styled with a circular shape, a background, and a stroke for a polished look.
- State Badge: If the state is not
.normal
, a badge is added at the bottom-right.
Usage Examples
Here’s how you can use the Avatar
component:
Display Initials
Avatar(type: .initials(text: "Aa"), size: .md, state: .online)
Display an Image
Avatar(type: .image(image: "ic_ProfileImage"), size: .lg, state: .offline)
Custom Size with an Icon
We can provide a custom size to the avatar like below and will get the desired sized avatar.
Avatar(type: .icon(icon: "ic_User"), size: .sm, state: .normal, customSize: 50)
Avatar Group Component
An Avatar Group is a common UI element used in collaborative and social applications to visually represent a group of users. This SwiftUI-based component is designed to handle scenarios where multiple avatars need to be displayed together while maintaining a clean and intuitive layout.
In this article, we’ll break down the AvatarGroup component and explore how it handles different group sizes, offsets, and layouts.
Component Overview
The AvatarGroup
is a SwiftUI component that:
- Accepts a list of
AvatarType
objects representing individual avatars. - Dynamically displays one or two avatars, with a badge indicating additional group members if more are present.
- Offers two predefined sizes:
.xs
(extra small) and.lg
(large).
The AvatarGroupSize Enum
enum AvatarGroupSize {
case xs
case lg
var offset: CGFloat {
switch self {
case .xs: return 20
case .lg: return 32
}
}
}
This enum defines two sizes for the avatar group:
.xs
: Small avatars with a smaller offset for tighter spacing..lg
: Larger avatars with increased spacing for better visibility.
The offset
property determines how far additional avatars or badges are positioned from the base avatar.
The AvatarGroup View
The AvatarGroup
struct is the core of the component. It takes three parameters:
items
: An array ofAvatarType
that defines the avatars to display.size
: An optionalAvatarGroupSize
, defaulting to.lg
.
Here’s a closer look at the logic inside the component:
struct AvatarGroup: View {
let items: [AvatarType]
var size: AvatarGroupSize = .lg
public var body: some View {
let itemCount = items.count
if items.count == 0 {
EmptyView()
} else {
ZStack {
if itemCount > 0 {
Avatar(type: items[0], size: size == .lg ? .sm : .xs, state: .normal)
}
Group {
if itemCount > 2 {
Circle()
.fill(Color.grey200)
.frame(width: size == .lg ? 40 : 32, height: size == .lg ? 40 : 32)
.overlay {
Text("\(items.count - 1)")
.foregroundStyle(.black)
.font(.system(size: 12, weight: .semibold))
.lineLimit(1)
.padding(2)
}
.padding([.leading, .top], size.offset)
} else if itemCount > 1 {
Avatar(type: items[1], size: size == .lg ? .sm : .xs, state: .normal)
.padding([.leading, .top], size.offset)
}
}
}
}
}
}
- Flexible Group Representation: The
AvatarGroup
displays up to two individual avatars or condenses excess avatars into a count badge for clarity and simplicity. - Configurable Sizes: Offers two size options,
.lg
and.xs
, to adapt the component for different UI contexts, such as compact lists or profile headers. - Dynamic Avatar Display: Integrates the
Avatar
component to render each avatar type (e.g., icons, initials, or images) seamlessly within the group. - Count Badge for Overflow: Automatically displays a circular badge showing the remaining number of avatars when the group exceeds two members.
Usage Examples
Here’s how to use the AvatarGroup
component:
1. Icons Avatar Group
AvatarGroup(
items: [
.icon(icon: "ic_User"),
.icon(icon: "ic_User")
],
size: .lg
)
2. Initials Avatar Group
AvatarGroup(
items: [
.initials(text: "Aa"),
.initials(text: "Aa")
],
size: .lg
)
3. Images Avatar Group
AvatarGroup(
items: [
.image(image: "ic_ProfileImage"),
.image(image: "ic_ProfileImage")
],
size: .lg
)
4. Overflow Avatar Group
AvatarGroup(
items: [
.image(image: "ic_ProfileImage"),
.image(image: "ic_ProfileImage"),
.image(image: "ic_ProfileImage"),
.image(image: "ic_ProfileImage"),
.image(image: "ic_ProfileImage"),
.image(image: "ic_ProfileImage"),
.image(image: "ic_ProfileImage")
],
size: .lg
)
Conclusion
The Avatar
and AvatarGroup
components are versatile and essential building blocks for modern user interfaces, particularly in collaborative and social applications. The Avatar
component offers customizable options for representing users through icons, initials, or images, while seamlessly integrating status indicators like online/offline badges. The AvatarGroup
extends this functionality, enabling the display of multiple avatars in a clean, dynamic layout that adapts to the number of members and available space. Together, these components enhance the user experience by providing visually consistent and intuitive elements for representing individuals and groups in any app.
You can get complete code from following link
https://gist.github.com/muhammadabbas001/5437eb59e7f9cccf02072a08609e6b88