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

SwiftUI实现跨视图圆形头像与指定顶部圆角的视图问题

SwiftUI个人中心界面:头像下方视图添加左上/右上圆角问题

我正在学习SwiftUI,要实现类似截图的个人中心界面,已经完成背景图和圆形头像的设置,但无法为头像下方的视图添加左上、右上圆角,不清楚该为哪个视图设置。以下是我的实现代码:

struct ProfileMainView: View {
    @EnvironmentObject var profile: ProfileViewModel
    @EnvironmentObject var carouselState: CarouselStateModel
    @EnvironmentObject var profileManager: ProfileManager
    @State var showExpose: Bool = false
    @State private var editMode = false
    @State private var viewMode = true
    @State private var shouldDisplayProfileCreation = false
    
    @State private var errorMessage = ""
    @State private var showError = false
    
    @EnvironmentObject var shippingAddressManager: ShippingAddressManager
    @State private var authDelegate: AuthDelegate?
    @State private var searchText = ""
    @State private var isEditMode = false
    
    @EnvironmentObject var userPaymentManager: UserPaymentManager
    
    let payments = [
        PayModel(number: "4444", fullname: "", source: PaymentTender.Source.visa),
        PayModel(number: "1414", fullname: "", source: PaymentTender.Source.visa),
        PayModel(number: "2020", fullname: "", source: PaymentTender.Source.visa)
    ]
    
    let fav = ["beltBar", "beltBar"]
    
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            VStack {
                // MARK: - Top View
                VStack {
                    ZStack {
                        Image("backgroundImage")
                            .resizable()
                            .frame(width: UIScreen.main.bounds.width, height: 150, alignment: .center)
                            .background(Color.charcoalBg)
                            .clipped()
                            .overlay(
                                HStack(alignment: .center) {
                                    if showExpose {
                                        
                                        WebImage(url: URL(string: profile.photo))
                                            .onSuccess { image, data, cacheType in
                                            }
                                            .resizable()
                                            .placeholder(Image("profilePic"))
                                            .indicator(.activity)
                                            .transition(.fade(duration: 0.5))
                                            .scaledToFill()
                                            .frame(width: 100)
                                            .overlay(Circle().stroke(Color.white, lineWidth: 5))
                                            .clipShape(Circle())
                                        
                                    } else {
                                        WebImage(url: URL(string: profile.photo))
                                            .onSuccess { image, data, cacheType in
                                            }
                                            .resizable()
                                            .placeholder(Image("profilePic"))
                                            .indicator(.activity)
                                            .transition(.fade(duration: 0.5))
                                            .scaledToFill()
                                            .frame(width: 100)
                                            .overlay(Circle().stroke(Color.white, lineWidth: 5))
                                            .clipShape(Circle())
                                        
                                    }
                                }
                                    .frame(height: 100, alignment: .center)
                                    .offset(y: 75)
                            )
                        Spacer()
                        HStack {
                            Spacer()
                            Button {
                                print("notificationOnProfile button action")
                            } label: {
                                Image("notificationOnProfile")
                            }
                        }
                        .padding(.trailing, 30)
                        .padding(.top, 30)
                    }
                }
                
                // MARK: - Profile Name View
                VStack(spacing: 5.0) {
                    HStack(spacing: 4.0) {
                        Text("Profile Name")
                            .font(.custom("Roboto-Medium", size: (showExpose ? 16 : 16)))
                            .foregroundColor(.black)
                        Image("star")
                    }
                    Text("Email")
                        .font(.custom("Roboto-Regular", size: 12))
                        .foregroundColor(Color.fontGrayProfile.opacity(0.6))
                }
                .padding(EdgeInsets(top: 45, leading: 0, bottom: 0, trailing: 0))
                
                // MARK: - Payments View
                VStack(alignment: .leading, spacing: 10) {
                    HStack {
                        Text("Payments")
                            .foregroundColor(Color.fontGrayProfile)
                            .font(.custom("Roboto-Medium", size: 14))
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding(.leading, 20)
                            .padding(.top, 14)
                        
                        Button {
                            print("")
                        } label: {
                            Image("Pencil")
                                .resizable()
                                .frame(width: 20.0, height: 20.0)
                                .padding(.trailing, 22)
                                .padding(.top, 14)
                        }
                    }
                    Divider().background(Color.black.opacity(0.1))
                        .padding([.leading, .trailing], 20)
                        .padding(.bottom, 4)
                    
                    VStack(alignment: .leading, spacing: 17) {
                        ForEach(payments) { int in
                            Text("int\(int.title)")
                                .padding(.bottom, 20)
                        }
                    }
                }
                .frame(maxWidth: .infinity)
                .cornerRadius(14)
                .background(
                    Rectangle()
                        .fill(Color.white)
                        .cornerRadius(14)
                        .shadow(
                            color: Color(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, opacity: 0.1),
                            radius: 20,
                            x: 0,
                            y: 0
                        )
                )
                .padding(EdgeInsets(top: 0, leading: 20, bottom: 20, trailing: 20))
                
                
                // MARK: - Shipping Address View
                VStack(alignment: .leading, spacing: 10) {
                    HStack {
                        Text("Shipping Address")
                            .foregroundColor(Color.fontGrayProfile)
                            .font(.custom("Roboto-Medium", size: 14))
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding(.leading, 20)
                            .padding(.top, 14)
                        
                        Button {
                            print("")
                        } label: {
                            Image("Pencil")
                                .resizable()
                                .frame(width: 20.0, height: 20.0)
                                .padding(.trailing, 22)
                                .padding(.top, 14)
                        }
                    }
                    Divider().background(Color.black.opacity(0.1))
                        .padding([.leading, .trailing], 20)
                        .padding(.bottom, 4)
                    
                    VStack(alignment: .leading, spacing: 17) {
                        ForEach(payments) { int in
                            Text("int\(int.title)")
                                .padding(.bottom, 20)
                        }
                    }
                }
                .frame(maxWidth: .infinity)
                .cornerRadius(14)
                .background(
                    Rectangle()
                        .fill(Color.white)
                        .cornerRadius(14)
                        .shadow(
                            color: Color(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, opacity: 0.1),
                            radius: 20,
                            x: 0,
                            y: 0
                        )
                )
                .padding(EdgeInsets(top: 0, leading: 20, bottom: 20, trailing: 20))
            }
            .alert(isPresented: $showError) {
                Alert(title: Text(AppConstants.appName), message: Text(errorMessage), dismissButton: .default(Text("OK")))
            }
            .navigationBarHidden(true)
        }
        .edgesIgnoringSafeArea(.all)
        
    }
}

解决方案

要实现头像下方视图的左上、右上圆角,你需要把**头像下方的所有内容(Profile Name、Payments、Shipping Address等)**统一包裹在一个容器中,给这个容器设置顶部圆角,具体操作如下:

  1. 新建一个VStack,将Profile Name View、Payments View、Shipping Address View全部移入其中,给这个容器设置白色背景和仅顶部的圆角:
// 替换原代码中头像下方的三个View部分
VStack {
    // MARK: - Profile Name View
    VStack(spacing: 5.0) {
        HStack(spacing: 4.0) {
            Text("Profile Name")
                .font(.custom("Roboto-Medium", size: (showExpose ? 16 : 16)))
                .foregroundColor(.black)
            Image("star")
        }
        Text("Email")
            .font(.custom("Roboto-Regular", size: 12))
            .foregroundColor(Color.fontGrayProfile.opacity(0.6))
    }
    .padding(EdgeInsets(top: 45, leading: 0, bottom: 0, trailing: 0))
    
    // MARK: - Payments View
    VStack(alignment: .leading, spacing: 10) {
        // 原Payments View内容
    }
    .frame(maxWidth: .infinity)
    .background(Color.white)
    .cornerRadius(14)
    .shadow(color: Color.black.opacity(0.1), radius: 20, x: 0, y: 0)
    .padding(EdgeInsets(top: 0, leading: 20, bottom: 20, trailing: 20))
    
    // MARK: - Shipping Address View
    VStack(alignment: .leading, spacing: 10) {
        // 原Shipping Address View内容
    }
    .frame(maxWidth: .infinity)
    .background(Color.white)
    .cornerRadius(14)
    .shadow(color: Color.black.opacity(0.1), radius: 20, x: 0, y: 0)
    .padding(EdgeInsets(top: 0, leading: 20, bottom: 20, trailing: 20))
}
.background(Color.white)
.cornerRadius(14, corners: [.topLeft, .topRight])
.clipped()
  1. 简化原卡片的背景设置:移除原background中的Rectangle,直接给容器设置background(Color.white)和cornerRadius,避免重复设置导致的视觉冲突。

  2. 确保最外层布局衔接自然:顶部背景图和下方圆角容器之间不要留间隙,这样就能实现背景图下方接带顶部圆角的白色区域,和目标效果一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:22:00