Unexplained Gap in SwiftUI View - swiftui

I am seeing a white line between two of my view elements, that I cannot explain.
The following cod is the main SwiftUI View
var body: some View {
NavigationView {
VStack {
TextField("Filter", text: $lastNameFilter)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding([.top, .leading, .trailing, .bottom])
.background(Color(UIColor.systemGroupedBackground))
FilteredList(filter: lastNameFilter)
} }
The FilteredList view is very simple:
var body: some View {
List {
ForEach(fetchRequest, id: \.self) { recipient in
NavigationLink(destination:
ViewEventsView(recipient: recipient)) {
Text("\(recipient.wrappedFirstName) \(recipient.wrappedLastName)")
.foregroundColor(.green)
}
}
.onDelete(perform: deleteRecipient)
}
}
I have tried with and without padding, but that is not the issue. The .padding, is adjusting the inset of the "filter" TextField.
Any pointers would be appreciated.

That's probably the default spacing of the VStack. Try changing it to VStack(spacing: 0).

Related

Prevent SwiftUI Divider expanding horizontally

I have the following setup:
struct ContentView: View {
var body: some View {
VStack {
Text("Title").font(.title)
HStack {
Text("Hello:").bold()
Text("World")
}
}
.padding()
.background {
RoundedRectangle(cornerRadius: 8).stroke(.black)
}
}
}
which renders as follows:
When I add a Divider() into the VStack…
struct ContentView: View {
var body: some View {
VStack {
Text("Title").font(.title)
Divider() // Added this
HStack {
Text("Hello:").bold()
Text("World")
}
}
.padding()
.background {
RoundedRectangle(cornerRadius: 8).stroke(.black)
}
.padding()
}
}
… it forces the VStack to expand horizontally as large as possible.
How do I make the Divider fit to the other VStack content?
Having written the question, I tried some more things, and found a solution. Leaving here in case it is useful for others.
I just needed to add
.fixedSize(horizontal: true, vertical: false)
to the VStack, which makes all the subviews have the same horizontal size, resulting in…

SwiftUI About ZStack & NaviagtonLink

I use ZStack & NaviagtonLink in SwiftUI.
ZStack{
List{
NavigationLink(destination: NextView()) {
Text("Hi")
}
}
Text("Hello")
}
This view is valid as I expected, and there are the text "Hello" on List's columns.
But even in NavigationLink ZStack is valid and in NextView there is hello.
I want to display it only this view, and I don't want to display it in NextView,
Please tell me how to fix.
Thank you.
Embed in navigationView otherwise navigation link cannot work
struct ContentView: View {
var body: some View {
NavigationView{
ZStack{
List{
NavigationLink(destination: NextView()) {
Text("Hi")
}
}
Text("Hello")
}
.navigationBarHidden(true)
}
}
}

Navigation title not appearing correctly in SwiftUI

I have a VStack wrapped around a NavigationView. I made a NavigationTitle by adding the modifier to VStack. However, my title is not appearing near the top of the screen as it should.
Here is my code:
NavigationView{
VStack{
Image(club.image)
.resizable()
.scaledToFit()
.frame(height: 300)
Text(club.name)
.font(.system(size: 40, weight: .black))
HStack(alignment: .center, spacing: 20){
Label(title: {
Text(club.league)
.foregroundColor(.secondary)
}, icon: {
Image(systemName: "location.north.circle.fill")
.foregroundColor(.blue)
})
Label(title: {
Text(club.netWorth)
.foregroundColor(.secondary)
}, icon: {
Image(systemName: "dollarsign.circle.fill")
.foregroundColor(.blue)
})
}
}.navigationTitle(club.name)
}
I have tried adding the '.navigationTitle' modifier to the NavigationView as well, but that isn't working.
Here is an image as well:
Navigation title image
Does anybody have a solution to this?
As lorem ipsum has mentioned. It's as simple as removing the extra NavigationView. When using a Navigation Link it's assumed that it's within a NavigationView. So you only need to declare it once in the root view. If you wanted additional NavigationLink you'd add it without a NavigationView.
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("Chelsea")
NavigationLink("Second View Link", destination: SecondView())
}
.navigationTitle("Chelsea")
}
}
}
struct SecondView: View {
var body: some View {
VStack {
Text("Second View")
}
.navigationTitle("Second View")
}
}

swiftui list not working when put into ZStack

I use ZStack to combine a list and Color, after doing it, List will not scroll and there's no output when clicking the text.
Does anyone know how to fix it?
Thanks
struct ContentView: View {
var body: some View {
ZStack{
List{
ForEach(1...30, id: \.self){ i in
Text("ROW \(i)")
.font(.system(size: 40))
.onTapGesture {
print("clicked \(i)")
}
}
}
Color.black.opacity(0.2)
}
}
}
Move Color before List and it will work. See the altered code below.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack{
Color.black.opacity(0.2)
List{
ForEach(1...30, id: \.self) { i in
Text("ROW \(i)")
.font(.system(size: 40))
.onTapGesture {
print("clicked \(i)")
}
}
}
}
}
}
I don't know why it isn't working — probably a bug — but you can (and probably should) do this instead.
struct ContentView: View {
var body: some View {
List{
ForEach(1...30, id: \.self){ i in
Text("ROW \(i)")
.font(.system(size: 40))
.onTapGesture {
print("clicked \(i)")
}
}
}.background(Color.black.opacity(0.2))
}
}

SwiftUI NavigationButton without the disclosure indicator?

When making a List with a row that pushes to a new view, SwiftUI adds a disclosure indicator ">" automatically? How do I remove it if I don't want it?
NavigationView {
List {
NavigationButton(destination: DetailView()) {
ListItem()
}
}
.navigationBarTitle(Text("Some title"))
}
On a UITableViewCell you set Accessory to None but how do I do that in SwiftUI?
Setting the NavigationLink width and hiding it did the trick for me
List {
ForEach(pages) { page in
HStack(spacing: 0) {
Text("Something")
NavigationLink(destination: Text("Somewhere")) {
EmptyView()
}
.frame(width: 0)
.opacity(0)
}
}
}
Swift 5, Xcode 11. ZStack works perfect.
var body: some View {
NavigationView {
List {
ForEach(viewModel.currenciesViewModel) { cellViewModel in
ZStack {
cellViewModel.makeView()
NavigationLink(destination: ChooseCurrencyListView()) {
EmptyView()
}
.buttonStyle(PlainButtonStyle())
}
}
}
.navigationBarHidden(true)
.navigationBarTitle("", displayMode: .inline)
}
}
The easiest one. The content for each item in the list.
ZStack {
NavigationLink(destination: DetailView()) {
EmptyView()
}.hidden()
RowView()
}
As workaround I can suggest to add .padding modifier like this:
NavigationView {
List {
NavigationButton(destination: DetailView()) {
ListItem()
}
}
.navigationBarTitle(Text("Some title"))
}
.padding(.trailing, -32.0)
So you will get rows without visible disclosure:
You can also put it in the .background modifier:
List {
Text("Go to...")
.background(NavigationLink("", destination: Text("Detail View")))
}
If you already have the background modifier on the Text, you can wrap the Text in a HStack and apply background to the HStack.
What you can do, if you are using list, is setting the navigationlink to hidden and its frame width to zero.
HStack{
Button(action: {self.statusShow = 1}, label: {
Image(systemName: "info.circle")
})
NavigationLink(destination: StimulatorSettingView(),
tag: 1,
selection: self.$statusShow){
EmptyView()
}.hidden().frame(width: 0)
}
This worked for me.
As of beta 6, this works well:
struct SwiftUIView: View {
var body: some View {
NavigationView {
List {
HStack {
Text("My Cell Content")
NavigationLink(destination: Text("destination"), label: {
EmptyView()
})
}
}
}
}
}
You don't have to use NavigationLink to wrap your Label directly. It will work as long as the link is anywhere in your view hierarchy.
Here I've wrapped it in a button, which allows you to trigger an action prior to pushing the view. Since the NavigationLink has an EmptyView for the label the disclosure indicator is not visible. You can also style this with ButtonStyle.
struct NavigationButton<Destination: View, Label: View>: View {
var action: () -> Void = { }
var destination: () -> Destination
var label: () -> Label
#State private var isActive: Bool = false
var body: some View {
Button(action: {
self.action()
self.isActive.toggle()
}) {
self.label()
.background(NavigationLink(destination: self.destination(), isActive: self.$isActive) {
EmptyView()
})
}
}
}
And to use it:
NavigationButton(
action: { print("tapped!") },
destination: { Text("Pushed View") },
label: { Text("Tap me") }
)
NavigationLink is what we should define in a scope enclosed inside a NavigationView.
But when we use NavigationLink it is attached to the enclosing view, so to reuse the same NavigationLink with other views, we use tag which differentiates between different Destinations.
struct SwiftUIView: View {
#State private var viewState: Int? = 0
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("View 1"), tag: 1, selection: $viewState) {
EmptyView()
}
NavigationLink(destination: Text("View 2"), tag: 2, selection: $viewState) {
EmptyView()
}
Text("First View")
.onTapGesture {
self.viewState = 1
}
Text("Second View")
.onTapGesture {
self.viewState = 2
}
}
}
}
}
Here we bind a Hashable property with all the NavigationLinks present in our VStack so that when a particular View is tapped we can notify which Destination should be opened by setting the value of Bindable property.
If we don't notify the correct Destination by setting the value of tag, always the View defined inside the Closure of NavigationLink will be clickable and nothing else.
Using this approach you don't need to wrap all your clickable views inside NavigationView, any action on any view can use any NavigationLink just by setting the tag.
Thanks, hope this helps.
Works well for me!
import SwiftUI
struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
EmptyView()
}
}
.navigationBarTitle(Text("Landmarks"))
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
ForEach(["iPhone SE", "iPhone 11 Pro Max"], id: \.self) { deviceName in
LandmarkList()
.previewDevice(PreviewDevice(rawValue: deviceName))
.previewDisplayName(deviceName)
}
}
}
Use .frame(width: 0).opacity(0.0):
NavigationView {
List {
ForEach(options) {
option in
ZStack {
YourView(option: option)
NavigationLink(destination: ProductListView(),
label: {
EmptyView()
}).frame(width: 0).opacity(0.0)
}.listRowInsets(EdgeInsets())
}
}.navigationBarHidden(true)
}
My version of this solution is to make a view modifier. I think it's the cleanest way, as it doesn't use AnyView.
Note that this solution runs the init() for the destination when it draws the element the .navigationLink() is attached to.
Usage
Text("Link")
.navigationLink({
// put your destination here
})
How To
import SwiftUI
extension View {
func navigationLink<Destination: View>(_ destination: #escaping () -> Destination) -> some View {
modifier(NavigationLinkModifier(destination: destination))
}
}
fileprivate struct NavigationLinkModifier<Destination: View>: ViewModifier {
#ViewBuilder var destination: () -> Destination
func body(content: Content) -> some View {
content
.background(
NavigationLink(destination: self.destination) { EmptyView() }.opacity(0)
)
}
}
This helps to push and pass the model to the next navigation view controller.
struct ContentView : View {
#State var model = PostListViewModel()
var body: some View {
NavigationView {
List(model.post) { post in
ListCell(listData: post)
}.navigationBarTitle(Text("My Post"))
}
}
}
struct ListCell: View {
var listData: Post
var body: some View {
return NavigationButton(destination: DetailContentView(post: listData)) {
HStack {
ImageRow(model: listData) // Get image
VStack(alignment: .leading) {
Text(listData.login).font(.headline).lineLimit(nil)
Text(listData.url).font(.subheadline).lineLimit(nil)
}.padding(.leading, 10)
}.padding(.init(top: 5, leading: 0, bottom: 5, trailing: 0))
}
}
}
Here's a reusable "plain" navigation link view (i.e. without the chevron disclosure indicator) that can be a drop-in replacement for NavigationLink:
struct PlainNavigationLink<Label, Destination>: View where Label: View, Destination: View {
#ViewBuilder var destination: () -> Destination
#ViewBuilder var label: () -> Label
var body: some View {
label()
.background(
NavigationLink(destination: destination, label: {})
.opacity(0)
)
}
}
To use it, simply replace NavigationLink with PlainNavigationLink:
NavigationView { // or NavigationStack in iOS 16
List {
ForEach(1...30, id: \.self) { _ in
PlainNavigationLink {
Text("Hello, world!")
} label: {
Text("Hello, world!")
}
}
}
}
We can also extend it with convenience initializers for LocalizedStringKey and String, just like NavigationLink does.
just came here looking for the answer to this question, but none of the proposed solutions worked for me (can't have an empty view, because i want to put something in the list row; i'm already messing with the padding (and increasing trailing padding didn't seem to work) ... i was about to give up, and then something occurred to me: what if you crank up the z-index of the list row itself? seemed somewhat unlikely, but i gave it a try and, i'll be damned, it worked! i was so pleasantly surprised, i felt like sharing ...
e.g.:
// in body of your list row view
HStack(alignment: .top, spacing: 0.0) {
// stuff ...
}
.zIndex(9999999999)
If you need children behaviour for List and NavigationLink, without additional discloser in the same time, I want to promote this tricky solution, main point at HStack
var body: some View {
NavigationView {
List(items, children: \.items) { item in
ZStack {
NavigationLink(destination: DetailsView()) {
EmptyView()
}.hidden()
HStack {
RowView(item: item)
Spacer()
}
}
}
}
}
Once you put your button in a scrollview, the disclosure button will be hidden. Just make sure to disable your scroll indicator.
there is no documentation yet, so you can use ScrollView for now
NavigationView {
ScrollView {
ForEach(0...100){ x in
NavigationButton(destination: Text("ss")) {
HStack {
Text(String(x))
Spacer()
}
.padding()
.background(Color.white)
.shadow(radius:1,y:1)
}
}
.frame(width: UIScreen.main.bounds.width - 32)
.padding()
}
}
Removing List and just using ForEach works fine with navigation link. You just have to create your own list row. This works for me
NavigationView {
ForEach(pages) {
page in
NavigationLink(destination: DetailView()) {
ListItem()
}
}
}