0

How to make this using Swiftui:

Design

I tried to make this design in swiftui and it is resembling as well but i don't think this approach is a cleaner way. I am attaching my code below.

struct NewShapeView: View {
   
    let properties: [Property]
    
    var body: some View {
        ForEach(properties) { property in
            VStack(alignment: .leading, spacing: 0) {
                ForEach(property.zones.indices, id: \.self) { index in
                    LineView(zoneName: property.zones[index].name)
                    SubView(showVerticalLine: index != property.zones.indices.last)
                        
                }
            }
        }
    }
}

struct LineView: View {
    
    let zoneName: String
    
    var body: some View {
        HStack(alignment: .bottom, spacing: 0) {
            Rectangle().frame(width: 1, height: 40)
            Rectangle().frame(width: 40, height: 1)
            Text(zoneName)
        }
    }
}

struct SubView: View {
    
    var showVerticalLine: Bool
    
    var body: some View {
        HStack(spacing: 0) {
            if showVerticalLine {
                Rectangle().frame(width: 1, height: 40) // Extend the line through SubView
            }
            Spacer().frame(width:  32, height: 36) // Adjust the spacer width to align with the previous line
            Image(systemName: "speaker.wave.2.fill")
            Text("Some Paradise - L’impératrice")
                .font(.subheadline)
                .foregroundColor(.gray)
        }
    }
}

I am a beginner and working on my first production swiftui app. Any suggestions and help are welcomed. Thanks in advance.

I tried a hack of drawing a vertical line on the boundary of zone subview till last view is found in array. I am expecting a cleaner approach.

2
  • Please edit your question to clarify what is a "cleaner" approach. What is not clean about your current approach? Be specific. Commented Sep 22, 2024 at 6:25
  • Instead of hiding the line for last zone subview, i am looking for a way to first draw a horizontal line with zone subview and then from the last horizontal line could draw a vertical line using path. Is there any way to achieve this using paths? Commented Sep 22, 2024 at 14:13

1 Answer 1

0

Your code is perfectly valid and works, but there are some small issues:

  • As you pointed out, you are currently passing a flag to the SubView to indicate whether or not to show a vertical line. This could be avoided.

  • If the text changes size (due to dynamic fonts, or wrapping text) then the size of the lines might need some adjustment.

  • If you wanted to change the style of the lines to, say, dashed lines, there will probably not be a clean continuation of the dashes from one row to the next. This is because the lines will be stroked independently, so the size of the dash (or gap) may be inconsistent at the point where two rows join.

I would suggest thinking of the lines as decoration to the content. So instead of including the lines as part of the foreground content, you could move them to the background of the containers or text content. This way, the lines automatically adopt the sizes and positions of the views in the foreground.

Also:

  • Instead of iterating over the indices of the zones array, you might want to consider iterating over the enumerated array. See this answer for an explanation of how enumerated() works.

  • To define the gaps between the content, it may be simpler to apply padding to the containers, or to the content of the containers, rather than using Spacer with fixed width. You could also adjust the spacing of the VStacks as a way of tweaking the vertical spacing.

  • For the case of the gap below the vertical line, you could use a hidden version of the last SubView as placeholder. This will allow the necessary space to be reserved, but left empty.

Here is an updated example. The view LineView is no longer needed:

struct NewShapeView: View {
    let properties: [Property]
    let leadingGap: CGFloat = 40

    var body: some View {
        ForEach(properties) { property in
            VStack(alignment: .leading, spacing: 10) {
                ForEach(Array(property.zones.enumerated()), id: \.offset) { offset, zone in
                    VStack(alignment: .leading, spacing: 10) {
                        Text(zone.name)
                            .padding(.leading, leadingGap)
                            .background(alignment: .bottomLeading) {
                                Rectangle().frame(width: leadingGap, height: 1)
                            }
                        SubView(/*zone: zone*/)
                            .padding(.leading, leadingGap)
                    }
                    .padding(.top, 25)
                }
            }
            .background(alignment: .leading) {
                VStack(alignment: .leading, spacing: 10) {
                    Rectangle().frame(width: 1)
                    if let _ = property.zones.last {
                        SubView(/*zone: lastZone*/).hidden()
                    }
                }
            }
        }
    }
}

struct SubView: View {
    var body: some View {
        HStack(spacing: 0) {
            Image(systemName: "speaker.wave.2.fill")
            Text("Some Paradise - L’impératrice")
                .font(.subheadline)
                .foregroundColor(.gray)
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Looks better. Thanks for the answer. I will use this but still i am trying to play with paths and i will try to draw a vertical line from the last horizontal line start point.
@AbhishekChoudhary Pleased if you found the answer useful. You might like to consider creating a Shape consisting of a simple straight line (= a path that connects the min and max bounds) and use this in place of the Rectangle. This way, you could stroke the lines with different styles. However, trying to create a single path to include all lines will be difficult, unless you use fixed sizes. Using fixed sizes will not allow the text to grow in size if it needs to (see issues described in answer).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.