In my code, I want the indicators to not have a background but still be included in my UI. Therefore, I want this layout, but it has an issue—I cannot clip the ScrollView to remove those four extra parts. If I clip the ScrollView, I can get rid of the extra UI on the left side. However, since the row has padding on the right, the clipShape applies to the indicators instead of removing the unwanted extra UI. How can I maintain the UI style as it is while removing these four unnecessary parts?
import SwiftUI
struct ContentView: View {
private let layoutMargin: CGFloat = 5.0
private let cornerRadius: CGFloat = 5.0
var body: some View {
ScrollView(showsIndicators: true) {
VStack(spacing: layoutMargin) {
ForEach(0..<20, id: \.self) { value in
RoundedRectangle(cornerRadius: cornerRadius)
.fill(Color.white)
.frame(height: 20.0)
}
}
.padding(layoutMargin)
.padding(.trailing)
}
.background(
Color.blue
.clipShape(RoundedRectangle(cornerRadius: layoutMargin + cornerRadius))
.padding(.trailing)
)
.padding()
}
}
In my code, I want the indicators to not have a background but still be included in my UI. Therefore, I want this layout, but it has an issue—I cannot clip the ScrollView to remove those four extra parts. If I clip the ScrollView, I can get rid of the extra UI on the left side. However, since the row has padding on the right, the clipShape applies to the indicators instead of removing the unwanted extra UI. How can I maintain the UI style as it is while removing these four unnecessary parts?
import SwiftUI
struct ContentView: View {
private let layoutMargin: CGFloat = 5.0
private let cornerRadius: CGFloat = 5.0
var body: some View {
ScrollView(showsIndicators: true) {
VStack(spacing: layoutMargin) {
ForEach(0..<20, id: \.self) { value in
RoundedRectangle(cornerRadius: cornerRadius)
.fill(Color.white)
.frame(height: 20.0)
}
}
.padding(layoutMargin)
.padding(.trailing)
}
.background(
Color.blue
.clipShape(RoundedRectangle(cornerRadius: layoutMargin + cornerRadius))
.padding(.trailing)
)
.padding()
}
}
Share
Improve this question
edited Feb 3 at 10:03
Benzy Neez
23.9k3 gold badges15 silver badges45 bronze badges
asked Feb 2 at 22:35
MangoMango
556 bronze badges
0
3 Answers
Reset to default 1I was expecting that the modifier .contentMargins
could be used here, see contentMargins(_:_:for:)
. For example, something like:
ScrollView {
// ...
}
.clipShape(.rect(cornerRadius: 20))
.contentMargins(.trailing, 20, for: .scrollIndicators)
However, on macOS, this just adds extra padding around the scroll indicator, it doesn't place the indicator inside the specified margin. So I don't think it helps.
An alternative way to clip a view is to apply a mask. This actually gives you more flexibility than .clipShape
, because a mask can be a View
, it is not constrained to being a Shape
. So the mask can be built using an HStack
that combines a rounded rectangle (for clipping the scrolled content) with a square rectangle (to avoid clipping the scroll indicators).
Actually, you could consider using a Capsule
for masking the scroll indicator, this looks more natural than a rectangle with square corners.
When allowing for the scroll indicator, I think the width needs to be determined by trial and error, I don't think you can specify the width it should occupy (ref. notes above). I found that allowing a width of 14 works quite well:
ScrollView(showsIndicators: true) {
VStack(spacing: layoutMargin) {
// ...
}
.padding(layoutMargin)
}
.background {
Color.blue
.padding(.trailing, 14)
}
.mask {
HStack(spacing: 0) {
RoundedRectangle(cornerRadius: cornerRadius + layoutMargin)
Capsule()
.frame(width: 14)
}
}
.padding()
I made a custom shape for the issue.
import SwiftUI
struct ContentView: View {
private let layoutMargin: CGFloat = 5.0
private let cornerRadius: CGFloat = 5.0
var body: some View {
ScrollView(showsIndicators: true) {
VStack(spacing: layoutMargin) {
ForEach(0..<20, id: \.self) { value in
RoundedRectangle(cornerRadius: cornerRadius)
.fill(Color.white)
.frame(height: 20.0)
}
}
.padding(layoutMargin)
.padding(.trailing, 16.0)
}
.background(
Color.blue
.clipShape(RoundedRectangle(cornerRadius: layoutMargin + cornerRadius))
.padding(.trailing, 16.0)
)
.clipShape(RoundedRectWithCapsule(cornerRadius: layoutMargin + cornerRadius, capsuleWidth: 16.0))
.padding()
}
}
struct RoundedRectWithCapsule: Shape {
var cornerRadius: CGFloat
var capsuleWidth: CGFloat
func path(in rect: CGRect) -> Path {
var path = Path()
let capRect = CGRect(x: rect.maxX - capsuleWidth, y: rect.minY, width: capsuleWidth, height: rect.height)
let mainRect = CGRect(x: rect.minX, y: rect.minY, width: rect.width - capsuleWidth, height: rect.height)
path.addPath(RoundedRectangle(cornerRadius: cornerRadius).path(in: mainRect))
path.addPath(Capsule().path(in: capRect))
return path
}
}
You're encountering a common concern we tend to have when we layout our views. This is about the ordering of viewmodifiers, so you don't need .clipped()
(if you tried it in hopes of clipping the white bars inside the background).
In your code, you placed .padding()
after .background()
, which is the reason why the UI is not what you expected it to be. Placing it like so tells the compiler to add padding to the entire view, not adding padding to the background.
For example, if your body's content is like this:
var body: some View {
Button("Hello, world!") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(.red)
}
It will create a red box with a width and height of 200. But if you change the order like so:
var body: some View {
Button("Hello, world!") {
print(type(of: self.body))
}
.background(.red)
.frame(width: 200, height: 200)
}
You will not see a red box. Instead, you will see the red background just behind the text, yet its frame has a width and height of 200!
So, what you need to do is to place the .padding()
not after .background()
, but before it. This ensures that the white bars will stay inside the blue background. It will look a bit different, but I'm confident that you'll be able to solve it now that you know more about viewmodifier ordering.
If you want to know more about viewmodifier ordering: https://www.hackingwithswift/books/ios-swiftui/why-modifier-order-matters
Also, this was my code to make it closer to your expected view:
var body: some View {
ScrollView(showsIndicators: true) {
VStack(spacing: layoutMargin) {
ForEach(0..<20, id: \.self) { value in
RoundedRectangle(cornerRadius: cornerRadius)
.fill(Color.white)
.frame(height: 20.0)
}
}
.padding(.trailing)
.padding(.trailing)
}
.padding(layoutMargin)
.background(
Color.blue
.clipShape(RoundedRectangle(cornerRadius: layoutMargin + cornerRadius))
.padding(.trailing)
.padding(.trailing)
)
}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745254531a4618876.html
评论列表(0条)