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

SwiftUI中修剪裁剪圆形视图底部不可见区域的正确实现方案求助

Fixing the Trimmed Circle Layout Push Issue in SwiftUI

Hey there! I totally get this frustration—when you first move from UIKit to SwiftUI, it's easy to get tripped up by how the layout system handles views that draw only part of their intrinsic frame (like your trimmed circles). Let's break down why this is happening and fix it the "proper" way.

Why the Layout Is Breaking

Your trimmed Circle views might look like they're only showing a portion of the circle, but SwiftUI still treats them as full-square views (the intrinsic size of a Circle is a square matching its diameter). That invisible bottom portion of the circle is still taking up space in the layout, which pushes your entire HStack elements down to align with the full circle's bottom edge—not the visible part.

The clipping you tried didn't work because it only hides the content outside the view's frame; it doesn't change the frame itself. We need to either adjust the frame to match the visible content or tweak the alignment guides to ignore the invisible space.

Solution 1: Restrict the Circle View's Frame + Clip

This method explicitly sets the height of your circle container to match only the visible portion, then clips any content outside that bounds. This tells SwiftUI's layout system to only consider the visible height when positioning elements.

Here's the modified code:

private struct ActivityCircleComponent: View {
    private let widthOfCircle: CGFloat = 15
    private let headerIconSize = UIScreen.main.bounds.height * 0.03
    private let rotationAmount: Double = 152.5
    private let progress: Double = 0.4
    private let trimAmount: CGFloat = 40
    // Adjust this value to match your visible circle height (tweak visually)
    private let visibleCircleHeight: CGFloat = 120

    var body: some View {
        HStack(alignment: .bottom, spacing: 16) {
            ZStack(alignment: .center) {
                // Wrap the circles in a clipped frame to restrict height
                circleView
                    .frame(height: visibleCircleHeight)
                    .clipped()
                
                VStack {
                    Text("10.000")
                        .font(Fonts.Roboto.bold.of(size: 30))
                    Text("meters")
                        .font(Fonts.Roboto.light.of(size: 20))
                }
            }
            
            Image("Goal")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(height: headerIconSize)
        }
        .frame(maxWidth: .infinity) // Let the stack fill available width without forcing height
    }

    private var circleView: some View {
        ZStack(alignment: .center) {
            Circle()
                .trim(from: 0.0, to: 0.65)
                .stroke(style: StrokeStyle(lineWidth: widthOfCircle, lineCap: .round, lineJoin: .round))
                .foregroundColor(.black)
                .rotationEffect(Angle(degrees: rotationAmount))
            
            Circle()
                .trim(from: 0.0, to: progress)
                .stroke(style: StrokeStyle(lineWidth: widthOfCircle, lineCap: .round, lineJoin: .round))
                .foregroundColor(.red)
                .rotationEffect(Angle(degrees: rotationAmount))
        }
        .padding([.leading, .top, .trailing], widthOfCircle / 2)
    }
}

Key Changes:

  • Extracted the circle drawing into a reusable circleView subview for clarity
  • Added a visibleCircleHeight property to define how much of the circle should be visible (adjust this until your layout looks right)
  • Wrapped circleView in a .frame(height: visibleCircleHeight) and .clipped() to cut off the invisible bottom portion
  • Removed the forced full-height frame on the HStack—instead, let it size to content while filling available width with .frame(maxWidth: .infinity)
  • Added spacing: 16 to the HStack for better spacing between the circle and icon

Solution 2: Use Alignment Guides (No Clipping Needed)

If you don't want to hardcode a height, you can adjust the alignment guide for the circle view to tell the HStack where to align its bottom edge. This way, the layout system ignores the invisible bottom space entirely.

Modify the circleView like this:

private var circleView: some View {
    ZStack(alignment: .center) {
        // ... (same circle code as before)
    }
    .padding([.leading, .top, .trailing], widthOfCircle / 2)
    .alignmentGuide(.bottom) { dimensions in
        // Adjust this multiplier to match your visible bottom edge (e.g., 0.8 = 80% of full height)
        dimensions.height * 0.8
    }
}

Then keep the rest of the body code as in Solution 1 (remove the forced HStack height). This tells the HStack to align the circle's "bottom" to 80% of its full height, which corresponds to the visible portion's bottom edge.

Which Solution to Choose?

  • Use Solution 1 if you want precise control over the visible area and don't mind tweaking a height value. It's straightforward and makes the layout intent clear.
  • Use Solution 2 if you want to avoid hardcoding heights and prefer letting SwiftUI handle the layout, relying on alignment guides to adjust positioning.

Both are proper SwiftUI approaches—no hacks involved!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 19:52:51