Implementing Dark Mode Support for Rounded Collection View Sections in JJCollectionViewRoundFlowLayout

To enable automatic dark mode adaptation for rounded collection view sections, supply dynamic UIColor objects created with colorWithDynamicProvider: (Objective‑C) or UIColor { ... } (Swift) when building the JJCollectionViewRoundConfigModel in your delegate method.

The JJCollectionViewRoundFlowLayout library by kingjiajie provides an elegant way to draw rounded backgrounds behind collection view sections. When implementing dark mode support for these rounded sections, you do not need to modify the layout engine itself—instead, you configure the visual model to return adaptive colors that automatically resolve based on the current interface style.

How Dark Mode Works in the Layout

The library’s architecture already contains the necessary hooks to react to appearance changes. Three core components handle the rendering pipeline:

  • JJCollectionViewRoundConfigModel (JJCollectionViewRoundConfigModel.h) – Stores all visual parameters including backgroundColor, borderColor, and shadowColor.
  • JJCollectionReusableView – The decoration view that renders the actual rounded background.
  • JJCollectionViewRoundFlowLayout (JJCollectionViewRoundFlowLayout.m) – Coordinates between the delegate, attributes, and the decoration view.

When the system interface style changes, the layout automatically triggers color resolution through resolvedColorWithTraitCollection: (iOS 13+) and updates the view via toChangeCollectionReusableViewRoundInfoWithLayoutAttributes:.

The Delegate Contract

Your primary integration point is the delegate method collectionView:layout:configModelForSectionAtIndex:. The model returned here must contain dynamic UIColor instances rather than static constants. As implemented in kingjiajie/jjcollectionviewroundflowlayout, the layout stores this model inside JJCollectionViewRoundLayoutAttributes and passes it to the decoration view during applyLayoutAttributes:.

Automatic Trait Change Handling

Inside JJCollectionViewRoundFlowLayout.m, the decoration view implements traitCollectionDidChange: (lines 78‑84) to detect appearance switches:

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];
    [self toChangeCollectionReusableViewRoundInfoWithLayoutAttributes:_myCacheAttr];
}

This method re-invokes the color application logic, which resolves the dynamic colors for the new trait collection (lines 90‑102):

if (@available(iOS 13.0, *)) {
    view.layer.backgroundColor = [model.backgroundColor resolvedColorWithTraitCollection:self.traitCollection].CGColor;
    view.layer.shadowColor    = [model.shadowColor resolvedColorWithTraitCollection:self.traitCollection].CGColor;
    view.layer.borderColor    = [model.borderColor resolvedColorWithTraitCollection:self.traitCollection].CGColor;
}

Because this plumbing already exists, you only need to provide dynamic colors in the delegate.

Supplying Dynamic Colors in the Delegate

Return a JJCollectionViewRoundConfigModel instance where every color property uses a dynamic provider. This works for backgroundColor, borderColor, and shadowColor.

Objective‑C Implementation

- (JJCollectionViewRoundConfigModel *)collectionView:(UICollectionView *)collectionView
                                             layout:(UICollectionViewLayout *)layout
                         configModelForSectionAtIndex:(NSInteger)section {

    JJCollectionViewRoundConfigModel *model = [[JJCollectionViewRoundConfigModel alloc] init];

    // Dynamic background color
    model.backgroundColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull tc) {
        return tc.userInterfaceStyle == UIUserInterfaceStyleDark ?
               [UIColor colorWithWhite:0.15 alpha:1.0] :   // Dark mode
               [UIColor colorWithWhite:0.95 alpha:1.0];   // Light mode
    }];

    // Dynamic border color
    model.borderColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull tc) {
        return tc.userInterfaceStyle == UIUserInterfaceStyleDark ?
               [UIColor lightGrayColor] :
               [UIColor darkGrayColor];
    }];

    model.cornerRadius = 12.0;
    model.borderWidth  = 1.0;

    // Dynamic shadow color
    model.shadowColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull tc) {
        return tc.userInterfaceStyle == UIUserInterfaceStyleDark ?
               [UIColor blackColor] :
               [UIColor colorWithWhite:0.8 alpha:1.0];
    }];

    return model;
}

Swift Implementation

func collectionView(_ collectionView: UICollectionView,
                    layout: UICollectionViewLayout,
                    configModelForSectionAt section: Int) -> JJCollectionViewRoundConfigModel {

    let model = JJCollectionViewRoundConfigModel()
    model.backgroundColor = UIColor { trait in
        trait.userInterfaceStyle == .dark ?
            UIColor(white: 0.15, alpha: 1.0) :
            UIColor(white: 0.95, alpha: 1.0)
    }

    model.borderColor = UIColor { trait in
        trait.userInterfaceStyle == .dark ? .lightGray : .darkGray
    }

    model.cornerRadius = 12
    model.borderWidth  = 1

    model.shadowColor = UIColor { trait in
        trait.userInterfaceStyle == .dark ?
            .black :
            UIColor(white: 0.8, alpha: 1.0)
    }

    return model
}

Verifying the Integration

To confirm that dark mode support is active:

  1. Run the app on an iOS 13+ device or simulator.
  2. Navigate to the collection view using JJCollectionViewRoundFlowLayout.
  3. Toggle between Light and Dark appearance in Settings (or use the Environment Overrides panel in Xcode).
  4. The rounded section backgrounds should update immediately without requiring a reload or view recreation.

The example project included in the repository demonstrates this pattern in SixthViewController.m, which uses dynamic providers for section backgrounds.

Summary

  • Use dynamic UIColor objects created with colorWithDynamicProvider: or Swift trailing closure syntax when constructing JJCollectionViewRoundConfigModel.
  • The library handles trait changes automatically via traitCollectionDidChange: in JJCollectionReusableView and resolves colors using resolvedColorWithTraitCollection: (iOS 13+).
  • No modifications are required to JJCollectionViewRoundFlowLayout.m or the decoration view source code.
  • All color properties—backgroundColor, borderColor, and shadowColor—support dynamic values.

Frequently Asked Questions

Do I need to subclass JJCollectionViewRoundFlowLayout to support dark mode?

No. The layout already implements traitCollectionDidChange: and color resolution logic inside JJCollectionReusableView. You only need to return dynamic UIColor instances from the collectionView:layout:configModelForSectionAtIndex: delegate method.

What is the minimum iOS version required for this approach?

Dynamic colors using colorWithDynamicProvider: and resolvedColorWithTraitCollection: require iOS 13.0 or later. The library’s color resolution code is wrapped in @available(iOS 13.0, *) checks, so it remains safe on earlier iOS versions, though dynamic switching will not occur.

Can I use system semantic colors instead of custom dynamic providers?

Yes. You can assign system semantic colors like UIColor.systemBackground or UIColor.secondarySystemFill directly to the model properties. These colors are already dynamic and adapt automatically to the current interface style without requiring a custom provider block.

Why don't my rounded sections update when I toggle dark mode in Settings?

Ensure that every color property in your JJCollectionViewRoundConfigModel is a dynamic UIColor. Static colors (e.g., [UIColor whiteColor] or [UIColor blackColor]) do not respond to trait changes. Also verify that your delegate is correctly returning the model and that you are testing on iOS 13 or later.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →