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

SwiftUI:创建新增项时背景垂直下移的自定义动态列表

输入图片描述
输入图片描述

我已通过名为VectorShape的Shape结构体创建背景,并编写path方法绘制该形状,代码如下:

import SwiftUI

struct VectorShape: Shape {
        func path(in rect: CGRect) -> Path {
                var path = Path()
                let width = rect.size.width
                let height = rect.size.height
                path.move(to: CGPoint(x: 0.083771389*width, y: 0.0048507464*height))
                path.addLine(to: CGPoint(x: 0.0625162683*width, y: 0.0048507464*height))
                path.addCurve(to: CGPoint(x: 0.0579251837*width, y: 0.0703358247*height), control1: CGPoint(x: 0.0548233261*width, y: 0.0097014928*height), control2: CGPoint(x: 0.058639587*width, y: 0.0562963474*height))
                path.addCurve(to: CGPoint(x: 0.0625162683*width, y: 0.7512437811*height), control1: CGPoint(x: 0.0565676091*width, y: 0.0970149301*height), control2: CGPoint(x: 0.0625162683*width, y: 0.5410447761*height))
                path.addLine(to: CGPoint(x: 0, y: 0.7512437811*height))
                path.addCurve(to: CGPoint(x: 0.0021402598*width, y: 1.3582089552*height), control1: CGPoint(x: 0, y: 0.7512437811*height), control2: CGPoint(x: 0.0021406803*width, y: 1.1144278607*height))
                path.addLine(to: CGPoint(x: 0.083771389*width, y: 1.3582089552*height))
                path.addLine(to: CGPoint(x: 0.7362637363*width, y: 1.3582089552*height))
                path.addLine(to: CGPoint(x: 0.7362637363*width, y: 0))
                path.addLine(to: CGPoint(x: 0.083771389*width, y: 0.0048507464*height))
                path.closeSubpath()
                return path
        }
}

#Preview{
    VectorShape().fill().frame(width: 273, height: 201)
}

目前我存在以下疑问:

  • 如何在VectorShape内部创建可动态变化的列表,使得新增列表项时VectorShape背景可垂直向下移动

我曾尝试使用ZStack,在其中添加VectorShape和用于列表的VStack,但列表无法被VectorShape包含。因此我不清楚如何实现列表动态化,让VectorShape背景随新增列表项垂直下移,是否需要借助其他软件完成动态部分?

以下是我已完成的结构代码:

import SwiftUI

struct ChecklistItem: Identifiable {
    var id = UUID()
    var title: String
    var isChecked: Bool
}

struct ChecklistTest: View {
    @State private var checklistItems = [
            ChecklistItem(title: "Item 1", isChecked: false),
            ChecklistItem(title: "Item 2", isChecked: false),
            ChecklistItem(title: "Item 3", isChecked: false)
        ]
    
    var body: some View {
        ZStack{
            VectorShape()
                .fill(Color(#colorLiteral(red: 1, green: 0.96009022, blue: 0.8565915227, alpha: 1)))
                .stroke(.black)
                .frame(width: 300, height: 300)
            
            VStack {
                ForEach($checklistItems) { $item in
                    HStack {
                        Text(item.title)
                        Spacer()
                        Image(systemName: item.isChecked ? "checkmark.square" : "square")
                            .onTapGesture {
                                item.isChecked.toggle()
                            }
                    }
                    .padding(5)
                    
                    if item.id != checklistItems.last?.id {
                        Divider()
                            .overlay(.black)
                    }
                }
            }
            .frame(width: 280, height: 150, alignment: .leading)
            .padding(.top, 60)
        }
    }
}

#Preview {
    ChecklistTest()
}

解决方案

核心思路是让VectorShape作为列表容器的背景,而非独立的ZStack层,这样形状会自动跟随列表高度动态变化。修改后的完整代码如下:

import SwiftUI

struct ChecklistItem: Identifiable {
    var id = UUID()
    var title: String
    var isChecked: Bool
}

struct ChecklistTest: View {
    @State private var checklistItems = [
        ChecklistItem(title: "Item 1", isChecked: false),
        ChecklistItem(title: "Item 2", isChecked: false),
        ChecklistItem(title: "Item 3", isChecked: false)
    ]
    @State private var newItemTitle = ""

    var body: some View {
        VStack(spacing: 16) {
            // 带VectorShape背景的列表容器
            VStack {
                ForEach($checklistItems) { $item in
                    HStack {
                        Text(item.title)
                        Spacer()
                        Image(systemName: item.isChecked ? "checkmark.square" : "square")
                            .onTapGesture {
                                item.isChecked.toggle()
                            }
                    }
                    .padding(5)
                    
                    if item.id != checklistItems.last?.id {
                        Divider()
                            .overlay(.black)
                    }
                }
            }
            .padding(.top, 60) // 适配形状顶部的空白区域
            .padding(.bottom, 40) // 适配形状底部的延伸区域
            .padding(.horizontal, 20)
            .background(
                VectorShape()
                    .fill(Color(#colorLiteral(red: 1, green: 0.96009022, blue: 0.8565915227, alpha: 1)))
                    .stroke(.black)
            )
            .frame(width: 300) // 固定宽度,高度随列表内容自动调整
            
            // 新增列表项的输入框和按钮
            HStack {
                TextField("输入新项", text: $newItemTitle)
                    .textFieldStyle(.roundedBorder)
                Button("添加") {
                    guard !newItemTitle.isEmpty else { return }
                    checklistItems.append(ChecklistItem(title: newItemTitle, isChecked: false))
                    newItemTitle = ""
                }
                .buttonStyle(.borderedProminent)
            }
            .padding(.horizontal, 20)
        }
        .padding()
    }
}

#Preview {
    ChecklistTest()
}

关键修改说明

  1. 背景绑定:将VectorShape设置为列表容器的background,而非ZStack的独立层,这样形状会完全贴合容器的尺寸,列表高度变化时形状自动跟随。
  2. 移除固定高度:删掉了原代码中VectorShape的固定frame(height: 300),让它根据内容高度自适应。
  3. 适配内边距:调整列表的padding值,让内容和形状的轮廓对齐,避免内容超出形状范围。
  4. 新增功能:添加了输入框和按钮,方便测试新增列表项时的动态效果。

由于你的VectorShape是基于宽高比例绘制的路径,无论容器高度如何变化,形状的轮廓都会按比例缩放,完全满足新增列表项时背景垂直下移的需求,不需要额外软件辅助。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:57:17