Remove extra spacing on List - swift

I can't figure out how to make the swipeActions have the size of the view that is on dark, I tried placing a spacer below or text, and it just creates an ever bigger mess with some space. What would be the correct solution for this?
In case it's not clear, If you notice the purple square it's bigger than what's on it's left side, I want them both to be the same size.
List(someItems, id: \.self) { item in
VStack {
TheView()
}
.swipeActions(allowsFullSwipe: true) { // Adds custom swipe actions
Button {
} label: {
Label("Hide", systemImage: "eye.slash")
}
.tint(.accentColor)
}
.listRowBackground(Color(.red)) // Sets Row color or a custom color: .listRowBackground(Color("DarkBackground"))
.listRowInsets(EdgeInsets()) // Removes extra padding space in rows
.listRowSeparator(.hidden) // Removed lines between items
.cornerRadius(10)
.shadow(radius: 2)
.padding(.bottom)
}
.listStyle(PlainListStyle()) // Removes extra spaces around the whole List
The extra space at the bottom is because of the .padding(.bottom), if I remove it the size is correct, but the items would be all close together and I need some space.
Edit:
This is the view I'm using above called "TheView()", it's not much, I don't think the problem is there.
var body: some View {
VStack {
HStack {
HStack(alignment: .center) {
SomeImage()
}
HStack(alignment: .top) {
VStack {
HStack {
Text("sometext")
.foregroundColor(.primary)
.bold()
Spacer()
}
HStack {
Text(String(xyz!))
.font(.footnote)
.fontWeight(.light)
Spacer()
}
}
HStack {
Text("$515")
.font(.footnote)
.fontWeight(.light)
}
}
}
.padding(5)
.padding(.horizontal, 3)
}
.background(LinearGradient(gradient: Gradient(colors: [Color("SomeColor")]), startPoint: .topLeading, endPoint: .bottomTrailing))
}

Related

How to make a List background black in SwiftUI?

What I did to make the entire screen black is to use this construction in my view:
ZStack {
Color.black.ignoresSafeArea() }
And for the List modifiers I used the following two:
.listRowBackground(Color.black)
.listStyle(.plain)
This works great.
But then I want to add a view on top of my List (like a header) and some white space appears around it ? Where does that come from ? And how can I change it to black ?
Here is my complete code:
struct WorkoutView: View {
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
List {
TempView()
ForEach(1..<30) { index in
VStack(alignment: .leading, spacing: 4) {
Text("one line")
.foregroundColor(.white)
.font(.title)
.fontWeight(.bold)
}
.padding(.vertical)
}
.listRowBackground(Color.black)
}
.listStyle(.plain)
}
}
}
struct TempView: View {
var body: some View {
ZStack(alignment: .leading) {
Color.black.ignoresSafeArea()
Text("Hello World")
.foregroundColor(.red)
.font(.largeTitle)
.bold()
}
}
}
Just do the same thing you did for the ForEach: add a .listBackground() modifier. Like this:
TempView()
.listRowBackground(Color.black)

NavigationBarTitle automatic display mode doesn't work

I am unsure how to fix this. I have implemented a ScrollView and a NavigationView with ZStacks. However, the automatic display mode doesn't work and the ScrollView sits behind it, with the Title overlapping.
https://i.stack.imgur.com/KPkGh.png
https://i.stack.imgur.com/H8NwS.png
Here is my current code:
struct Overview: View {
var body: some View {
ZStack{
NavigationView {
ZStack {
Color.init("Background")
.navigationBarHidden(false)
.navigationBarTitle("Overview", displayMode: .automatic)
.edgesIgnoringSafeArea(.top)
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
ForEach(0..<10) {
Text("Item \($0)")
.foregroundColor(.white)
.font(.largeTitle)
.frame(width: 340, height: 200)
.background(ColorManager.BoxColor)
.cornerRadius(10)
}
}
.frame(maxWidth: .infinity)
}
}
}
}
}
}
var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
ForEach(0..<10) {
Text("Item \($0)")
.foregroundColor(.blue)
.font(.largeTitle)
.frame(width: 340, height: 200)
.background(Color(.gray))
.cornerRadius(10)
}
}
.frame(maxWidth: .infinity)
}
.navigationBarHidden(false)
.navigationBarTitle("Overview", displayMode: .automatic)
}
}
You had the navigation item in a ZStack. A ZStack is used to display overlapping views on top of each other.
I believe what you really were looking to achieve stacking the title above the scrollView. That would be achieved with VStack. That however, would still be wrong.
Because it's a navigational setting, it goes inline with the NavigationView. So it can be displayed in the NavigationBar. It doesn't need to be apart of any Stacks.

SwiftUI List/Form/ScrollView being clipped when offset

I changed the y offset of a list, and now it's being clipped.
I am trying to make it so that when you scroll, you can partially see the text underneath the Title and buttons at the top of the view. In other words, I want the top section of the screen to be slightly transparent.
I added the offset to the list, so that it didn't overlap with the information at the top.
The image above is with the VStack in my code showing. I thought that the VStack might be getting in the way, so I commented it out and the image below was the result:
Here's my code:
var body: some View {
ZStack {
VStack {
HStack {
Button(action: {self.showAccountView.toggle()}) {
Image(systemName: "person.fill")
.renderingMode(.original)
.font(.system(size: 20, weight: .bold))
.frame(width: 44, height: 44)
.modifier(NavButtons())
}
.sheet(isPresented: $showAccountView) {
AccountView()
}
Spacer()
Button(action: { self.showHelpCenter.toggle()}) {
Image(systemName: "questionmark")
.renderingMode(.original)
.font(.system(size: 20, weight: .bold))
.modifier(NavButtons())
}
.sheet(isPresented: $showHelpCenter) {
HelpCenter()
}
}
.frame(maxWidth: .infinity)
.padding(.horizontal)
Spacer()
}
List {
ForEach (store.allLogs) { thing in
VStack (alignment: .leading) {
HStack {
Text("\(thing.date) , \(thing.time)")
}
Text(thing.notes)
.multilineTextAlignment(.leading)
}
}
}.offset(y: 50)
}
}
EDIT:
This is one possible solution:
struct MyList: UIViewRepresentable {
func makeUIView(context: Context) -> UITableView {
let view = UITableView()
view.clipsToBounds = false
return view
}
func updateUIView(_ uiView: UITableView, context: Context) {
}
}
and then you would use makeUIView and updateUIView to update the cells. This is just messy and it's not really using SwiftUI at that point.
Second Edit
I've found this issue with a scrollView as well as a form:
The black is the background. Here's the code for all three:
Group {
List ((0 ... 10), id: \.self) {
Text("Row \($0)")
}
.offset(y: 200)
.border(Color.blue, width: 3)
.background(Color.black)
ScrollView {
Text("Text")
}
.foregroundColor(.white)
.offset(y: 200)
.border(Color.blue, width: 3)
.background(Color.black)
Form {
Text("Text")
}
.offset(y: 200)
.border(Color.blue, width: 3)
.background(Color.black)
}
Here are the wireframes of a List:
Here are the names of frames that are the same height as the List/Form/ScrollView:
List:
PlainList.BodyContent
ListCore.Container
ListRepresentable
View Host
TableWrapper
UpdateCoalescingTableView
Form:
GroupList.BodyContent
ListCore.Container
ListRepresentable
View Host
TableWrapper
UpdateCoalescingTableView
ScrollView:
ScrollViewBody
SystemScrollView
View Host
HostingScrollView
I guess that my question has changed from "how do I do this..." to "Why is this happening?"
I'm pretty confused about what exactly is going on.
UIScrollView.appearance().clipsToBounds = false
Stick this in the body of AppDelegate.swift -> applicationDidFinishLaunchingWithOptions(). It will make all scroll views across your application unclipped by default.
The problem is that a list has a table view (which is a scroll view) underneath. Scroll views by default are clipped. We just need to change that default.
Using LazyHStack instead of HStack solves the clipping problem.

Center and align SwiftUI elements in a ScrollView

I have the following SwiftUI view:
import SwiftUI
struct MySwiftUIView: View {
var body: some View {
VStack {
HStack {
Text("top leading text")
Spacer()
}
Spacer()
HStack {
Spacer()
Text("centered text")
Spacer()
}
Spacer()
HStack {
Spacer()
Text("bottom trailing text")
}
}
}
}
It looks like this when run:
If I embed the view in a ScrollView like this:
import SwiftUI
struct MySwiftUIView: View {
var body: some View {
ScrollView {
VStack {
HStack {
Text("top leading text")
Spacer()
}
Spacer()
HStack {
Spacer()
Text("centered text")
Spacer()
}
Spacer()
HStack {
Spacer()
Text("bottom trailing text")
}
}
}
}
}
Then it looks like this when run:
How do I make the centered text centered, and the bottom trailing text rest on the bottom, when they are embedded in a ScrollView?
In a way, I want to use SwiftUI to replicate this scrolling behaviour seen in Xcode's inspectors, where the text "Not Applicable" is centered and scrollable:
Wrap the ScrollView inside GeometryReader. Then apply a minimum height to the scroll view equal to the geometry.size.height. Now the spacers you applied should fill the VStack the way you intended them to. Try the code below:
GeometryReader { geometry in
ScrollView(.vertical) {
VStack {
HStack {
Text("top leading text")
Spacer()
}
Spacer()
HStack {
Text("centered text")
}
Spacer()
HStack(alignment: .bottom) {
Spacer()
Text("bottom trailing text")
}
}
.padding()
.frame(width: geometry.size.width)
.frame(minHeight: geometry.size.height)
}
}
Also check this post for more discussion on this issue: SwiftUI - Vertical Centering Content inside Scrollview

SwiftUI content is not displayed on top of the screen

I have a NavigationView and then a VStack. The issue is that everything inside VStack is being displayed in the middle of the screen and not on top. Why is that?
var body: some View {
VStack {
VStack(alignment: .leading) {
if (request?.receiverID ?? "") == userDataViewModel.userID {
Text("\(sendertFirstName) wants to ship your package").font(.title)
} else {
Text("You want to ship \(recieverFirstName)'s package").font(.title)
}
HStack{
Image(systemName: "clock.fill").font(.subheadline)
Text("\(createdAt, formatter: DateService().shortTime)").font(.subheadline).foregroundColor(.secondary)
}
HStack {
VStack(alignment: .leading) {
HStack {
Image(systemName: "person.fill").font(.subheadline)
Text(sendertFirstName).font(.subheadline).foregroundColor(.secondary)
}
}
Spacer()
VStack(alignment: .leading) {
HStack {
Image(systemName: "star.fill")
Text(rating).font(.subheadline).foregroundColor(.secondary)
}
}
}
}
VStack(alignment: .center) {
HStack {
Button("Accept") {
//print(self.request.createdAt)
//FirestoreService().respondToRequest(request: self.request.documentReference, status: "accepted")
}
.padding()
Button("Decline") {
//FirestoreService().respondToRequest(request: self.request.documentReference, status: "declined")
}
.foregroundColor(.red)
.padding()
}
}
ScrollView {
ForEach(messages) { (message: Message) in
if message.senderId == self.userDataViewModel.userID {
HStack {
Spacer()
Text(message.text).font(.subheadline).padding(7.5).background(self.blue).cornerRadius(30).foregroundColor(.white)
}.padding(.bottom, 2)
} else {
HStack {
Text(message.text).font(.subheadline).padding(7.5).background(self.gray).cornerRadius(30).foregroundColor(.white)
Spacer()
}.padding(.bottom, 2)
}
}
}
HStack {
TextField("Message", text: $inputMessage).padding(5).background(self.gray).cornerRadius(30).foregroundColor(.white)
Button(action: {
let msg: [String: Any] = [
"text": self.inputMessage,
"createdAt": Timestamp(),
"senderId": self.userDataViewModel.userID
]
self.reference.updateData([
"messages": FieldValue.arrayUnion([msg])
])
self.messages.append(Message(dictionary: msg))
self.inputMessage = ""
}) {
Image(systemName: "arrow.up.circle.fill").foregroundColor(self.blue).font(.title)
}
}
Spacer()
}
.padding()
.border(Color.red)
}
I want the content to start on top not on the middle. What am I doing wrong here?
You don't need the NavigationView - the way you have everything set up now you are wrapping the VStack in a NavigationView twice and the massive white space is the second empty navigation bar.
By default most Views take only the amount of space they need and they are placed in the center of their parent view. So if you want your content to be placed at the top, you need to add Spacer() as the last element in your VStack:
var body: some View {
VStack {
/* ... */
Spacer()
}
}
Spacer is one of the rare Views that takes all space offered to it by the parent view and will push all of your content upwards.
Maybe you can try this way:
NavigationView {
VStack {
...
}.frame(maxHeight:.infinity, alignment: .top)
.padding()
.border(Color.red).navigationBarTitle("", displayMode: .inline)
}
Add a .navigationBarHidden(true) to your VStack