Skip to main content
The CometChatMessageHeader component displays user or group details in the toolbar including avatar, name, status, and typing indicators. It also provides navigation controls and call buttons.
{
  "component": "CometChatMessageHeader",
  "package": "CometChatUIKitSwift",
  "import": "import CometChatUIKitSwift\nimport CometChatSDK",
  "description": "Displays user or group details in the toolbar with avatar, name, status, typing indicators, and navigation controls.",
  "inherits": "UIView",
  "primaryOutput": {
    "callback": "onBack",
    "type": "() -> Void"
  },
  "props": {
    "data": {
      "user": {
        "type": "User?",
        "default": "nil",
        "note": "User to display in header"
      },
      "group": {
        "type": "Group?",
        "default": "nil",
        "note": "Group to display in header"
      }
    },
    "callbacks": {
      "onBack": "() -> Void",
      "onError": "(CometChatException) -> Void"
    },
    "visibility": {
      "hideBackButton": { "type": "Bool", "default": false },
      "hideUserStatus": { "type": "Bool", "default": false },
      "hideVideoCallButton": { "type": "Bool", "default": false },
      "hideVoiceCallButton": { "type": "Bool", "default": false }
    },
    "viewSlots": {
      "listItemView": "(User?, Group?) -> UIView",
      "leadingView": "(User?, Group?) -> UIView",
      "titleView": "(User?, Group?) -> UIView",
      "subtitleView": "(User?, Group?) -> UIView",
      "trailView": "(User?, Group?) -> UIView"
    }
  },
  "events": [],
  "sdkListeners": [
    "onUserOnline",
    "onUserOffline",
    "onTypingStarted",
    "onTypingEnded"
  ],
  "compositionExample": {
    "description": "MessageHeader is typically used within CometChatMessages at the top of the chat screen",
    "components": ["CometChatMessageHeader", "CometChatMessageList", "CometChatMessageComposer"],
    "flow": "User views header → sees recipient info → taps back to return to conversations"
  },
  "types": {}
}

Where It Fits

CometChatMessageHeader displays the recipient’s information at the top of the chat screen. It’s typically used within CometChatMessages alongside CometChatMessageList and CometChatMessageComposer.
import UIKit
import CometChatUIKitSwift
import CometChatSDK

class ChatViewController: UIViewController {
    
    private var messageHeader: CometChatMessageHeader!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupMessageHeader()
    }
    
    private func setupMessageHeader(for user: User) {
        messageHeader = CometChatMessageHeader()
        messageHeader.set(user: user)
        
        // Handle back button
        messageHeader.set(onBack: { [weak self] in
            self?.navigationController?.popViewController(animated: true)
        })
        
        view.addSubview(messageHeader)
    }
}

Minimal Render

import CometChatUIKitSwift
import CometChatSDK

let messageHeader = CometChatMessageHeader()
messageHeader.set(user: user)

Actions and Events

Callback Props

onBack

Fires when the back button is pressed. Use this for custom navigation handling.
import CometChatUIKitSwift

let messageHeader = CometChatMessageHeader()

messageHeader.set(onBack: { [weak self] in
    self?.navigationController?.popViewController(animated: true)
})

onError

Fires when an error occurs.
import CometChatUIKitSwift

let messageHeader = CometChatMessageHeader()

messageHeader.set(onError: { error in
    print("Error: \(error.errorDescription)")
})

Actions Reference

MethodDescriptionExample
set(onBack:)Triggered when back button is pressedCustom navigation
set(onError:)Triggered when an error occursShow error alert
set(user:)Sets the user to displayConfigure header for user
set(group:)Sets the group to displayConfigure header for group

SDK Events (Real-Time, Automatic)

SDK ListenerInternal behavior
onUserOnlineUpdates status indicator to online
onUserOfflineUpdates status indicator to offline
onTypingStartedShows typing indicator in subtitle
onTypingEndedHides typing indicator

Custom View Slots

SlotSignatureReplaces
listItemView(User?, Group?) -> UIViewEntire header content
leadingView(User?, Group?) -> UIViewAvatar / left section
titleView(User?, Group?) -> UIViewName / title text
subtitleView(User?, Group?) -> UIViewStatus / subtitle text
trailView(User?, Group?) -> UIViewRight side (call buttons)

listItemView

Replace the entire header content with a custom view.
import UIKit
import CometChatUIKitSwift
import CometChatSDK

let messageHeader = CometChatMessageHeader()

messageHeader.set(listItemView: { user, group in
    let customView = CustomHeaderView()
    customView.configure(user: user, group: group)
    return customView
})
You can create a CustomHeaderView as a custom UIView:
import UIKit
import CometChatSDK

class CustomHeaderView: UIView {
    
    private let backButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(systemName: "chevron.left"), for: .normal)
        button.tintColor = .black
        return button
    }()
    
    private let profileImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.layer.cornerRadius = 18
        imageView.clipsToBounds = true
        imageView.backgroundColor = .lightGray
        return imageView
    }()
    
    private let nameLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
        label.textColor = .black
        return label
    }()
    
    private let statusLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 12)
        label.textColor = .gray
        return label
    }()
    
    private let videoCallButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(systemName: "video.fill"), for: .normal)
        button.tintColor = .black
        return button
    }()
    
    private let callButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(systemName: "phone.fill"), for: .normal)
        button.tintColor = .black
        return button
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupView() {
        backgroundColor = .white

        let userInfoStack = UIStackView(arrangedSubviews: [nameLabel, statusLabel])
        userInfoStack.axis = .vertical
        userInfoStack.spacing = 2
        userInfoStack.alignment = .leading

        let rightButtonsStack = UIStackView(arrangedSubviews: [videoCallButton, callButton])
        rightButtonsStack.axis = .horizontal
        rightButtonsStack.spacing = 16

        addSubview(backButton)
        addSubview(profileImageView)
        addSubview(userInfoStack)
        addSubview(rightButtonsStack)

        backButton.translatesAutoresizingMaskIntoConstraints = false
        profileImageView.translatesAutoresizingMaskIntoConstraints = false
        userInfoStack.translatesAutoresizingMaskIntoConstraints = false
        rightButtonsStack.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            backButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            backButton.centerYAnchor.constraint(equalTo: centerYAnchor),
            backButton.widthAnchor.constraint(equalToConstant: 30),
            backButton.heightAnchor.constraint(equalToConstant: 30),

            profileImageView.leadingAnchor.constraint(equalTo: backButton.trailingAnchor, constant: 10),
            profileImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
            profileImageView.widthAnchor.constraint(equalToConstant: 36),
            profileImageView.heightAnchor.constraint(equalToConstant: 36),

            userInfoStack.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 10),
            userInfoStack.centerYAnchor.constraint(equalTo: centerYAnchor),

            rightButtonsStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            rightButtonsStack.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])
    }
    
    func configure(user: User?, group: Group?) {
        nameLabel.text = user?.name ?? group?.name ?? "Chat"
        if let user = user {
            statusLabel.text = user.status == .online ? "Online" : "Offline"
        } else if let group = group {
            statusLabel.text = "\(group.membersCount) members"
        }
    }
}

leadingView

Customize the leading view (avatar area) of the header.
import UIKit
import CometChatUIKitSwift
import CometChatSDK

let messageHeader = CometChatMessageHeader()

messageHeader.set(leadingView: { user, group in
    let view = CustomLeadingView()
    return view
})
You can create a CustomLeadingView as a custom UIView:
import UIKit

class CustomLeadingView: UIView {

    private let profileImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.layer.cornerRadius = 20
        imageView.clipsToBounds = true
        imageView.backgroundColor = .lightGray
        imageView.image = UIImage(systemName: "person.fill")
        return imageView
    }()

    private let badgeLabel: UILabel = {
        let label = UILabel()
        label.text = "Admin"
        label.font = UIFont.systemFont(ofSize: 10, weight: .bold)
        label.textColor = .white
        label.backgroundColor = .orange
        label.textAlignment = .center
        label.layer.cornerRadius = 6
        label.clipsToBounds = true
        return label
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupView() {
        addSubview(profileImageView)
        addSubview(badgeLabel)

        profileImageView.translatesAutoresizingMaskIntoConstraints = false
        badgeLabel.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            profileImageView.leadingAnchor.constraint(equalTo: leadingAnchor),
            profileImageView.trailingAnchor.constraint(equalTo: trailingAnchor),
            profileImageView.topAnchor.constraint(equalTo: topAnchor),
            profileImageView.bottomAnchor.constraint(equalTo: bottomAnchor),
            profileImageView.heightAnchor.constraint(equalToConstant: 40),
            profileImageView.widthAnchor.constraint(equalToConstant: 40),

            badgeLabel.trailingAnchor.constraint(equalTo: profileImageView.trailingAnchor),
            badgeLabel.bottomAnchor.constraint(equalTo: profileImageView.bottomAnchor),
            badgeLabel.widthAnchor.constraint(equalToConstant: 40),
            badgeLabel.heightAnchor.constraint(equalToConstant: 12)
        ])
    }
}

titleView

Customize the title view of the header.
import UIKit
import CometChatUIKitSwift
import CometChatSDK

let messageHeader = CometChatMessageHeader()

messageHeader.set(titleView: { user, group in
    let view = CustomTitleView()
    return view
})
You can create a CustomTitleView as a custom UIView:
import UIKit

class CustomTitleView: UIView {

    private let titleLabel: UILabel = {
        let label = UILabel()
        label.text = "Artistic Design"
        label.font = UIFont.systemFont(ofSize: 16, weight: .medium)
        label.textColor = .black
        return label
    }()

    private let publicBadge: UIView = {
        let view = UIView()
        view.backgroundColor = .blue
        view.layer.cornerRadius = 10

        let icon = UIImageView(image: UIImage(systemName: "person.3.fill"))
        icon.tintColor = .white
        icon.contentMode = .scaleAspectFit

        let label = UILabel()
        label.text = "Public"
        label.font = UIFont.systemFont(ofSize: 12, weight: .bold)
        label.textColor = .white

        let stackView = UIStackView(arrangedSubviews: [icon, label])
        stackView.spacing = 4
        stackView.alignment = .center
        stackView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(stackView)

        NSLayoutConstraint.activate([
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            view.widthAnchor.constraint(equalToConstant: 60),
            view.heightAnchor.constraint(equalToConstant: 20)
        ])
        
        return view
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupView() {
        addSubview(titleLabel)
        addSubview(publicBadge)

        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        publicBadge.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
            titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor),

            publicBadge.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 6),
            publicBadge.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])
    }
}

subtitleView

Customize the subtitle area (status, typing indicator).
import UIKit
import CometChatUIKitSwift
import CometChatSDK

let messageHeader = CometChatMessageHeader()

messageHeader.set(subtitleView: { user, group in
    let label = UILabel()
    label.font = UIFont.systemFont(ofSize: 13)
    label.textColor = UIColor.secondaryLabel
    
    if let user = user {
        label.text = user.status == .online ? "🟢 Online" : "⚫ Offline"
    } else if let group = group {
        label.text = "\(group.membersCount) members"
    }
    
    return label
})

trailView

Customize the right side of the header (call buttons, etc.).
import UIKit
import CometChatUIKitSwift
import CometChatSDK

let messageHeader = CometChatMessageHeader()

messageHeader.set(trailView: { user, group in
    let view = CustomTrailView()
    return view
})
You can create a CustomTrailView as a custom UIView:
import UIKit

class CustomTrailView: UIView {

    private let videoCallButton: UIButton = {
        let button = UIButton(type: .system)
        let image = UIImage(systemName: "video.fill")?.withRenderingMode(.alwaysTemplate)
        button.setImage(image, for: .normal)
        button.tintColor = .black
        return button
    }()

    private let callButton: UIButton = {
        let button = UIButton(type: .system)
        let image = UIImage(systemName: "phone.fill")?.withRenderingMode(.alwaysTemplate)
        button.setImage(image, for: .normal)
        button.tintColor = .black
        return button
    }()
    
    private let bookMarkButton: UIButton = {
        let button = UIButton(type: .system)
        let image = UIImage(systemName: "bookmark.fill")?.withRenderingMode(.alwaysTemplate)
        button.setImage(image, for: .normal)
        button.tintColor = .black
        return button
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupView() {
        let buttonsStack = UIStackView(arrangedSubviews: [videoCallButton, callButton, bookMarkButton])
        buttonsStack.axis = .horizontal
        buttonsStack.spacing = 16
        buttonsStack.distribution = .fillEqually

        addSubview(buttonsStack)
        buttonsStack.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            buttonsStack.leadingAnchor.constraint(equalTo: leadingAnchor),
            buttonsStack.trailingAnchor.constraint(equalTo: trailingAnchor),
            buttonsStack.topAnchor.constraint(equalTo: topAnchor),
            buttonsStack.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
}

Styling

Style Hierarchy

  1. Global styles (CometChatMessageHeader.style) apply to all instances
  2. Instance styles override global for specific instances

Global Level Styling

import UIKit
import CometChatUIKitSwift

// Apply global styles that affect all CometChatMessageHeader instances
CometChatMessageHeader.style.backgroundColor = UIColor.systemBackground
CometChatMessageHeader.style.titleTextColor = UIColor.label
CometChatMessageHeader.style.titleTextFont = UIFont.systemFont(ofSize: 17, weight: .semibold)
CometChatMessageHeader.style.subtitleTextColor = UIColor.secondaryLabel
CometChatMessageHeader.style.subtitleTextFont = UIFont.systemFont(ofSize: 13)
CometChatMessageHeader.style.backButtonImageTintColor = UIColor.systemBlue

// Custom avatar style
let avatarStyle = AvatarStyle()
avatarStyle.cornerRadius = CometChatCornerStyle(cornerRadius: 20)
CometChatMessageHeader.style.avatarStyle = avatarStyle

Instance Level Styling

import UIKit
import CometChatUIKitSwift

// Create a custom style for a specific instance
var customStyle = MessageHeaderStyle()
customStyle.backgroundColor = UIColor(red: 0.95, green: 0.95, blue: 0.97, alpha: 1.0)
customStyle.titleTextColor = UIColor.darkGray
customStyle.titleTextFont = UIFont.systemFont(ofSize: 18, weight: .bold)
customStyle.subtitleTextColor = UIColor.gray

let messageHeader = CometChatMessageHeader()
messageHeader.style = customStyle

Key Style Properties

PropertyTypeDefaultDescription
backgroundColorUIColorCometChatTheme.backgroundColor01Background color
borderWidthCGFloat0Border width
borderColorUIColor.clearBorder color
cornerRadiusCometChatCornerStyle?nilCorner radius
titleTextColorUIColorCometChatTheme.textColorPrimaryTitle text color
titleTextFontUIFontCometChatTypography.Heading4.mediumTitle font
subtitleTextColorUIColorCometChatTheme.textColorSecondarySubtitle text color
subtitleTextFontUIFontCometChatTypography.Body.regularSubtitle font
backButtonImageTintColorUIColorCometChatTheme.iconColorPrimaryBack button tint
backButtonIconUIImage?System chevronBack button icon
privateGroupBadgeImageTintColorUIColorCometChatTheme.iconColorSecondaryPrivate group badge tint
passwordProtectedGroupBadgeImageTintColorUIColorCometChatTheme.iconColorSecondaryPassword group badge tint
avatarStyleAvatarStyleDefault avatar styleAvatar customization

Customization Matrix

What to changeWhereProperty/APIExample
Background colorStylebackgroundColorUIColor.systemBackground
Title appearanceStyletitleTextColor, titleTextFontCustom colors and fonts
Subtitle appearanceStylesubtitleTextColor, subtitleTextFontCustom colors and fonts
Back buttonStylebackButtonIcon, backButtonImageTintColorCustom icon and color
Avatar lookStyleavatarStyleAvatarStyle() with custom radius
Hide back buttonPropertyhideBackButtonheader.hideBackButton = true
Hide statusPropertyhideUserStatusheader.hideUserStatus = true
Custom subtitleView Slotset(subtitleView:)See Custom View Slots

Props

All props are optional. Sorted alphabetically.

hideBackButton

Hides the back button in the header.
TypeBool
Defaultfalse

hideUserStatus

Hides the user status (online/offline/last active).
TypeBool
Defaultfalse

hideVideoCallButton

Hides the video call button.
TypeBool
Defaultfalse

hideVoiceCallButton

Hides the voice call button.
TypeBool
Defaultfalse

Events

The MessageHeader component does not emit any global UI events.

Date Time Formatter

Customize how timestamps appear in the message header (e.g., “Last seen at…”) using the CometChatDateTimeFormatter.

Global Level Formatting

import CometChatUIKitSwift

// Customize time format for last seen
CometChatMessageHeader.dateTimeFormatter.time = { timestamp in
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    let formatter = DateFormatter()
    formatter.dateFormat = "h:mm a"
    return "Last seen at " + formatter.string(from: date)
}

// Customize today format
CometChatMessageHeader.dateTimeFormatter.today = { timestamp in
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    let formatter = DateFormatter()
    formatter.dateFormat = "h:mm a"
    return "Last seen today at " + formatter.string(from: date)
}

// Customize yesterday format
CometChatMessageHeader.dateTimeFormatter.yesterday = { timestamp in
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    let formatter = DateFormatter()
    formatter.dateFormat = "h:mm a"
    return "Last seen yesterday at " + formatter.string(from: date)
}

Instance Level Formatting

import CometChatUIKitSwift

let messageHeader = CometChatMessageHeader()

messageHeader.dateTimeFormatter.otherDay = { timestamp in
    let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
    let formatter = DateFormatter()
    formatter.dateFormat = "MMM d, yyyy"
    return "Last seen on " + formatter.string(from: date)
}

Available Formatters

FormatterPurposeDefault Format
timeStandard time displayh:mm a
todayLast seen todayToday at h:mm a
yesterdayLast seen yesterdayYesterday at h:mm a
lastweekLast seen within last weekDay name
otherDayLast seen older datesMMM d, yyyy

Troubleshooting

IssueSolution
Header not showing user infoEnsure user or group is set on the header
Status not updatingCheck SDK connection and user presence listeners
Back button not workingVerify onBack callback is set and hideBackButton is false
Typing indicator not showingEnsure typing events are enabled in SDK
Call buttons not appearingCheck that hideVideoCallButton and hideVoiceCallButton are false