在 SwiftUI 中的列表视图旁边绘制带有圆圈的垂直线
Posted
技术标签:
【中文标题】在 SwiftUI 中的列表视图旁边绘制带有圆圈的垂直线【英文标题】:Draw verticle Line with circle along side listview in SwiftUI 【发布时间】:2021-08-31 14:18:25 【问题描述】:我正在努力实现
这将是与列表视图一起运行的垂直线。列表视图中每个单元格的中间都有一个小圆圈。
我试过了
GeometryReader geometry in
VStack
Path path in
path.move(to: CGPoint(x: geometry.size.height / 2, y: 3))
path.addLine(to: CGPoint(x: geometry.size.height / 2, y: geometry.size.width))
.stroke(style: StrokeStyle( lineWidth: 3))
.foregroundColor(Color(UIColor.systemBlue))
这会垂直创建一条实线,但无法达到预期的效果,也无法弄清楚如何在每个内容的中间放置圆圈。
更新: 我尝试了下面的代码,它接近我的 UX 要求
import SwiftUI
struct VerticleLineCircle: View
let array: Array<Int> = Array(0...10)
public var body: some View
ScrollView
VStack
ForEach(0..<array.count) index in
GeometryReader geometry in
VStack
Path path in
path.move(to: CGPoint(x: geometry.size.height / 2, y: -10))
path.addLine(to: CGPoint(x: geometry.size.height / 2, y: geometry.size.width))
.stroke(style: StrokeStyle( lineWidth: 2))
.foregroundColor(Color(UIColor.systemBlue))
.padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 0))
Text("Hello, here is some custom view!")
HStack
Circle().fill(Color.blue)
.frame(width: 10, height: 10)
.position(x: 15, y: 0)
Line()
.stroke(style: StrokeStyle(lineWidth: 0.5))
.frame(height: 1)
.foregroundColor(Color.gray.opacity(80))
struct Line: Shape
func path(in rect: CGRect) -> Path
var path = Path()
path.move(to: CGPoint(x: -150, y: -5))
path.addLine(to: CGPoint(x: rect.width, y: 0))
return path
struct SwiftUIView3_Previews: PreviewProvider
static var previews: some View
VerticleLineCircle()
但仍然缺少一些东西 即在索引 0 和最后我希望线在视图中间以圆圈结束
【问题讨论】:
我认为使用 List 是不可能的,也许以自定义方式。 您已经更改了要求,并且您的代码有很多事情要做,这需要大量的重构。您的所有值都是硬编码的,并且可能会根据设备产生不一致的视图。 @loremipsum:当我们更新所有问题的答案时,我认为你和我都会成为百万富翁。这么好的付款!我不能忽视它。 @swiftPunk OP 在他们出现时删除了评论。这对我来说是拼图的快感。这是一个简单的。 @loremipsum:是的,他也对我做了同样的事,当我得到这样的 OP 时,我也会时不时地发生这种情况,他们在讨价还价,却忘记了我们花时间为了这!不知道!如果我在不使用路径的情况下云回答它,我只是把这个问题回答得很有趣!否则我会删除我的答案,也许我会保留它以供其他人查看或使用。 【参考方案1】:这是另一种使用自定义方式的方法,我称之为 VerticalLineInForEachView,我们不能使用 List 来完成这项工作:
struct ContentView: View
let array: Array<Int> = Array(0...10)
var body: some View
VerticalLineInForEachView(sizeOfCircle: 20.0, widthOfLine: 2.0, padding: 5.0, colorOfCircle: .black, colorOfLine: .red, showDivider: true, array: array) index in
HStack
CustomView(index: index)
Spacer()
.padding()
.background(Color.yellow.opacity(0.2).cornerRadius(16))
.padding(.vertical, 5.0)
.padding(.trailing)
.padding(.leading, 5.0)
struct VerticalLineInForEachView<Content: View, T>: View
let sizeOfCircle: CGFloat
let widthOfLine: CGFloat
let padding: CGFloat
let colorOfCircle: Color
let colorOfLine: Color
let showDivider: Bool
let array: Array<T>
@ViewBuilder let content: (Int) -> Content
var body: some View
ScrollView
LazyVStack(alignment: .leading, spacing: 0.0)
let upperBound: Int = array.count - 1
ForEach(array.indices, id: \.self, content: index in
VStack content(index)
.padding(.leading, (sizeOfCircle/2.0 + padding))
.overlay(((index != upperBound) && (index != 0)) ? colorOfLine.frame(width: widthOfLine).offset(x: -widthOfLine/2.0) : nil , alignment: .leading)
.overlay(((index == upperBound) || (index == 0)) ?
GeometryReader proxy in
colorOfLine
.frame(width: widthOfLine, height: proxy.size.height/2.0)
.offset(x: -widthOfLine/2.0, y: (index == 0) ? proxy.size.height/2.0 : 0.0)
: nil , alignment: .leading)
.overlay(Circle().fill(colorOfCircle).frame(width: sizeOfCircle, height: sizeOfCircle).offset(x: -sizeOfCircle/2.0) , alignment: .leading)
.overlay(((index != upperBound) && showDivider) ? Color.secondary.opacity(0.2).frame(height: 2).offset(y: 1).padding(.horizontal).padding(.leading, sizeOfCircle) : nil, alignment: .bottom)
)
.padding(.leading, sizeOfCircle/2.0)
struct CustomView: View
let index: Int
var body: some View
let randomSize: CGFloat = CGFloat.random(in: 50.0...250.0)
if index.isMultiple(of: 2)
if Bool.random()
Circle().fill(Color.blue).frame(width: randomSize, height: randomSize, alignment: .center)
else
Image(systemName: "books.vertical").resizable().scaledToFit().frame(width: randomSize, height: randomSize, alignment: .center)
else
if Bool.random()
Text(" Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
else
Text("Hello, World!")
【讨论】:
@AT9:我的回答很好,不需要更新,你不能为了其他问题不时更新你的问题,或者解决小问题!感谢意味着在这里投票或接受它作为答案。【参考方案2】:不适用于List
,因为它有自定义的padding
会留下空白,但您可以在ScrollView
中使用VStack
来做到这一点
import SwiftUI
struct CustomListView: View
///How far from the left edge
let start: CGFloat = 15
///Padding between margin and your row content
let padding: CGFloat = 16
var body: some View
ScrollView
VStack(spacing: 0)
ForEach(0..<10) n in
VStack
//if the item is the first element
if n == 0
CustomRowMarginView(start: start, rightPadding: padding, postion: .start)
Text("Your row Content goes here \(n)")
//if the item is the last element
else if n == 9
CustomRowMarginView(start: start, rightPadding: padding, postion: .end)
Text("Your row Content goes here \(n)")
//if the item is the middle element
else
CustomRowMarginView(start: start, rightPadding: padding, postion: .mid)
Text("Your row Content goes here \(n)")
.frame(height: CGFloat(Int.random(in: 20...100)))
Divider().padding(.leading, start + padding)
struct CustomRowMarginView<Content:View>: View
///How far from the left edge
let start: CGFloat
///Padding between margin and your row content
let rightPadding: CGFloat
let color: Color = Color(UIColor.systemBlue)
let content: Content
let postion: CustomLineShape.Position
init(start: CGFloat, rightPadding: CGFloat, postion: CustomLineShape.Position = .mid, @ViewBuilder _ content: @escaping () -> Content)
self.start = start
self.rightPadding = rightPadding
self.postion = postion
self.content = content()
var body: some View
HStack
ZStack(alignment: .leading)
CustomLineShape(start: start, postion: postion)
.stroke(style: StrokeStyle( lineWidth: 3))
.foregroundColor(color)
CustomDotShape(start: start, postion: postion)
.fill(color)
.frame(width: start + rightPadding)
content
Spacer()
struct CustomLineShape: Shape
let start: CGFloat
let postion: Position
func path(in rect: CGRect) -> Path
var path = Path()
if postion == .start || postion == .mid
path.move(to: CGPoint(x: start, y: postion == .start ? rect.midY : rect.minY))
path.addLine(to: CGPoint(x: start, y: rect.maxY))
else
path.move(to: CGPoint(x: start, y: rect.midY))
path.addLine(to: CGPoint(x: start, y: rect.minY))
return path
enum Position
case start
case end
case mid
struct CustomDotShape: Shape
let start: CGFloat
let postion: CustomLineShape.Position
func path(in rect: CGRect) -> Path
var path = Path()
if postion == .start
path.move(to: CGPoint(x: start, y: rect.midY ))
else
path.move(to: CGPoint(x: start, y: rect.minY))
path.addRelativeArc(center: CGPoint(x: start, y: postion == .start || postion == .end ? rect.midY : rect.minY), radius: 5, startAngle: Angle(degrees: 0), delta: Angle(degrees: 360), transform: .identity)
return path
struct CustomListView_Previews: PreviewProvider
static var previews: some View
CustomListView()
【讨论】:
以上是关于在 SwiftUI 中的列表视图旁边绘制带有圆圈的垂直线的主要内容,如果未能解决你的问题,请参考以下文章