What is the default value of the padding modifier in swift?
As far as I understood from Apple's documentation, there's no standard value and it's calculated based on some criteria by Apple. So, it may be different for different devices, accessibility settings of user, if user is using the app in side-by-side mode on iPad, etc...
Here is the documentation:
The set of edges along which to pad this view; if nil the specified or system-calculated amount is applied to all edges.
Left and right padding (not leading and trailing) in SwiftUI
Use @Environment(\.layoutDirection)
to get the current layout direction (LTR or RTL) and use it to flip .leading
and .trailing
as needed.
Here’s a ViewModifier
that wraps all that conveniently:
enum NoFlipEdge {
case left, right
}
struct NoFlipPadding: ViewModifier {
let edge: NoFlipEdge
let length: CGFloat?
@Environment(\.layoutDirection) var layoutDirection
private var computedEdge: Edge.Set {
if layoutDirection == .rightToLeft {
return edge == .left ? .trailing : .leading
} else {
return edge == .left ? .leading : .trailing
}
}
func body(content: Content) -> some View {
content
.padding(computedEdge, length)
}
}
extension View {
func padding(_ edge: NoFlipEdge, _ length: CGFloat? = nil) -> some View {
self.modifier(NoFlipPadding(edge: edge, length: length))
}
}
Use it like you would the standard padding modifiers:
Text("Text")
.padding(.left, 10)
SwiftUI - Ternary operator on padding modifier crashes program
Try this:
ForEach(0..<arr.count) { i in
ZStack {
...
}
.padding(.top, getPadding(i))
// I use i later in this code
...
}
func getPadding(_ i: Int) -> CGFloat {
if i == 0 {
return CGFloat(70)
}
return CGFloat(0)
}
How to remove the left and right Padding of a List in SwiftUI?
It looks like .listRowInsets
doesn't work for rows in a List
that is initialised with content
.
So this doesn't work:
List(items) { item in
ItemRow(item: item)
.listRowInsets(EdgeInsets())
}
But this does:
List {
ForEach(items) { item in
ItemRow(item: item)
.listRowInsets(EdgeInsets())
}
}
Remove default padding from List in SwiftUI
To achieve this you need to use ForEach
inside List
combined with .listRowInsets
as in example below
struct Demo: View {
var colors: [Color] = [.red, .blue, .yellow]
var body: some View {
List {
ForEach(colors, id: \.self) { color in
color
}.listRowInsets(EdgeInsets())
}
// Update: Different iOS versions have different
// default List styles, so set explicit one if needed
.listStyle(PlainListStyle())
}
}
SwiftUI container view - how to provide default value for a property?
You said “when I'd needed n optional arguments, I'd have to make n^2 initializers ...” It's worse than that, because it's 2^n (exponential), not n^2 (quadratic). But, n here is the number of generic arguments for which you want to provide defaults, which means n is usually quite small (1 for your SampleBgContainer
).
You also said “I can't imagine this is how SwiftUI is implemented”, but yes, that's exactly how SwiftUI does it when SwiftUI wants to provide a default for a generic argument. For example, SwiftUI provides a Toggle
initializer that lets you use a string instead of a View
for the label. It converts the string into a Text
for you, and it's declared like this:
extension Toggle where Label == Text {
public init(_ titleKey: LocalizedStringKey, isOn: Binding<Swift.Bool>)
}
Anyway, there is a solution to the exponential explosion of init
overloads, and we can find it by looking at SwiftUI's Text
.
You can tweak the appearance of a Text
in lots of ways. For example, you can make it bold or italic, you can change the kerning or the tracking, and you can change the text color. But you don't pass any of these settings to Text
's initializer. You do them all by calling modifiers on the Text
. Some of modifiers (like foregroundColor
) apply to any View
, but others (like bold
, italic
, kerning
, and tracking
) are only available on Text
directly.
You can use the same system: custom modifiers that work only on your type, instead of init
arguments. This interface style lets you avoid the 2^n overload explosion.
First, omit all of the non-generic, defaultable arguments from init
:
struct SampleContainer<Content: View, Background: View>: View {
var alignment: HorizontalAlignment = .leading
var padding: CGFloat = VisualStyle.stackItemPadding
var cornerRadius: CGFloat = VisualStyle.stackItemCornerRadius
var content: Content
var background: Background
init(
@ViewBuilder content: () -> Content,
@ViewBuilder background: () -> Background
) {
self.content = content()
self.background = background()
}
var body: some View {
VStack(alignment: alignment) {
content
}
.padding(.all, self.padding)
.frame(idealWidth: .infinity, maxWidth: .infinity)
.background(background)
.clipShape(RoundedRectangle(cornerRadius: self.cornerRadius))
}
}
Second, provide a single init
overload that constrains all of the defaultable generic arguments to their default types. In your case, there's only one such argument:
extension SampleContainer where Background == Color {
init(
@ViewBuilder content: () -> Content
) {
self.content = content()
self.background = Color.white.opacity(0.05)
}
}
Finally, provide modifiers for changing the properties, including the generic arguments:
private func with(_ mutate: (inout Self) -> ()) -> Self {
var copy = self
mutate(©)
return copy
}
func stackAlignment(_ alignment: HorizontalAlignment) -> Self {
return self.with { $0.alignment = alignment }
}
func stackPadding(_ padding: CGFloat) -> Self {
return self.with { $0.padding = padding }
}
func stackRadius(_ radius: CGFloat) -> Self {
return self.with { $0.cornerRadius = radius }
}
func stackBackground<New: View>(@ViewBuilder _ background: () -> New) -> SampleContainer<Content, New> {
return .init(content: { content }, background: background)
}
}
Use it like this for default settings:
SampleContainer {
Text("default settings only")
Text("hello world")
}
Or use the modifiers to customize it:
SampleContainer {
Text("custom settings only")
Text("hello world")
}
.stackAlignment(.trailing)
.stackPadding(2)
.stackRadius(20)
.stackBackground {
LinearGradient(
colors: [
Color.red,
Color.blue,
],
startPoint: .top,
endPoint: .bottom
)
}
How to add if else statement on padding? SwiftUI
Use ternary operator right inside modifier, like
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
.padding(.top, showBar ? 0 : -15) // << here !!
Related Topics
How to Map Over The Values of Zip2
Create a Generic Swift Function to Return an Array of Core Data Entities
How to Change Orientation for Avcapturemoviefileoutput in Swift
Swift: Rsa Encrypt a String with a Specific Private Key
Swiftycam Capture Session Is Not Running
Sizing a UIpickerview Inside a UIalertview
How Is Commoncrypto Used in Swift3
Crash Casting Wknsurlrequest As? Other Type
Macos Security Scoped Url Bookmark for Folder
Get Path of a File in a Data Set Located in Assets.Xcassets
Include Dictionary or Array Value in Swift String with Backslash Notation
How to Mirror The Design of The Codable/Codablekeys Protocols
No Exact Matches in Call to Instance Method Error Message in Swift
iOS App Extension - Action - Custom Data
Carthage Update Error: "Github API Request Failed: Bad Credentials"