SwiftUI: NavigationLink causes unwanted padding in minimal 10-line-example - swiftui

When wrapping views in a NavigationLink to create a Master-Detail flow, the NavigationLink appears to add padding (red) that I don't want.
If I comment out the NavigationLink, everything looks correct (picture on the right). That however of course makes my views unclickable.
What can I do to remove the red padding that occurs when I render this on the Watch?
This is the code that I run:
struct ContentView: View {
let testArray = [1,2,3,4,5]
var body: some View {
ScrollView {
VStack {
ForEach(testArray, id: \.self) { element in
NavigationLink(destination: Text("")) {
Text("BB . . . . . . . . . . . . . . . . . . . . . . .")
.font(.title3)
.foregroundColor(.white)
.background(Color.blue)
}.background(Color.red) //has all the unwanted padding
}
}.background(Color.black)
}.background(Color.gray)
}
}
I found a few good hints at How to show NavigationLink as a button in SwiftUI however doing ZStack/.background tricks only leaves a ~30px strip that is clickable, it does not fill the whole ZStack in that case.
I figured out the padding seems to be a static 8px. So what I'm doing now is .padding(.all, -8) hoping for a better solution to come

Apply .buttonStyle(PlainButtonStyle()) to your NavigationLink to remove the padding and background. You’ll also lose a button’s default styling, obviously, so you’ll have to recreate it if you want it.

To take away the unwanted border introduced by the NavigationLink, simply apply buttonStyle as plain to the NavigationLink like so:
NavigationLink(destination: DestinationView()) {
ExtractedView()
}.buttonStyle(.plain)

#Adam 's solution seems to doesn't be enough. Try the following code:
NavigationLink(destination: Text("")) {
Text("Dummy Text")
.font(.title3)
.foregroundColor(.white)
.background(Color.blue)
}
.buttonStyle(.plain)
.listRowInsets(EdgeInsets())

Related

gutters on sides of List in WatchOS

I'm dipping my toe into SwiftUI and WatchOS for the first time. I'm making good progress, but I can't figure out how to get rid of the black "gutters" on either side of my Image controls. I've tried setting all the backgrounds to white, but the gutter persists.
What property on which view do I need to set to change the color of the gutters to match the background?
SwiftUI
struct ContentView: View {
var body: some View {
List {
Image("cat-1").resizable().scaledToFill().background(Color.white)
Image("cat-2").resizable().scaledToFit().padding(5).background(Color.white)
Image("cat-3").resizable().scaledToFit().padding(.top, 5).background(Color.white)
}.background(Color.white).listStyle(CarouselListStyle())
.background(Color.white)
}
}
Try adding a
.listRowPlatterColor(.clear)
put it inside the list like this...
struct ContentView: View {
var body: some View {
List {
Image("cat-1")
.resizable()
.scaledToFit()
.listRowPlatterColor(.clear)
Image("cat-2").resizable().scaledToFit().padding(5).background(Color.white)
Image("cat-1")
.resizable()
.scaledToFit()
.padding(.top, 5)
.background(Color.white)
}
.listStyle(CarouselListStyle())
}
}
I put it in the first item and left it off of the second and third so that you could see the difference. This other question can provide some more details:
How to style rows in SwiftUI List on WatchOS? .
You should then be able to style it however you like.

Navigation View Formatting Trouble

I'm a newbie, using XCode 13.0 to create a very basic app that needs to have a Settings view. I'd like to navigate to the Settings view on tapping a label. To do that, it seemed sensible to use a NavigationView with a NavigationLink.
Unfortunately, I'm encountering a formatting issue that creates a mess of the HStack in which the Setting label (gear icon) resides, as show below:
This is what I want, a result of the following code:
HStack(spacing: 25) {
... other labels
Label ("", systemImage: "gear")
.foregroundColor(.gray)
.font(.title)
.onTapGesture(perform: {
// Set a state variable that triggers an extension
// that brings up the SettingsView
})
}
This is what happens when NavigationView encapsulates the gear icon label. Note the vertical and horizontal white space around it.
HStack(spacing: 25) {
... other labels
NavigationView {
NavigationLink(destination: SettingsView()) {
Label ("", systemImage: "gear")
.foregroundColor(.gray)
.font(.title)
}.navigationBarTitle(Text(""))
}
}
I've, literally, spent weeks (sporadically) on this issue, looking up dozens of answers and trying various formatting options, without luck. I've also tried encapsulating parent and grandparent stacks into the NavigationView. To no avail. Surely, this is something trivial. Can somebody point me in the right direction?
p.s. there are other issues in that that Navigation link opens as a sub-window; I plan to tackle that later.
Edit: Right, so I tried using Yrb's code:
HStack(spacing: 25) {
... other labels
NavigationView {
NavigationLink(destination: Text("Linked View")) {
Image(systemName: "gear")
.foregroundColor(.gray)
.font(.title)
}
.fixedSize()
.background(Color.red)
}
]
Unfortunately, there's no substantive change...
In diagnosing these sort of issues, it helps to throw a .background() with a color on. You can then see the issue. In this case, it was twofold, one, you need to use a .fixedSize to shrink the view to its smallest dimensions necessary. That would leave you with the icon plus a little space. That was due to you using a label as it was leaving a spot for the Text("") that you used as a fill in. Since you only want the image, use Image(systemName:) The code then comes out like this:
struct NavLinkNoSpace: View {
var body: some View {
NavigationView {
NavigationLink(destination: Text("Linked View")) {
Image(systemName: "gear")
.foregroundColor(.gray)
.font(.title)
}
.fixedSize()
// Setting this shows you what space you are using. Remove it when you are done
.background(Color.red)
}
}
}
A couple more things. If you have not ever set the NavigationTitle, you don't need to set it to "". In your example, there was no title, so I simply removed it and there was no effect.
More importantly, and it was addressed by some of the comments, you should only have one NavigationView in the view hierarchy. As long as you are in the hierarchy, you do not need to wrap things like NavigationLink to have them work. You can always throw one around your view call in the preview provider if you are in a child view, to show what things look like, and to test NavigationLinks, etc., but do not just put them in to your main code. It will lead to undesirable outcomes.
To summarize what worked to fix the primary problem, that of formatting: The key was in figuring what to encapsulate within the NavigationView. My mistake was to assume that only the NavigationLink needed to be in the NavigationView.
What worked was to place all the contents of the body into the NavigationView, like below:
var body: some View {
NavigationView {
VStack(spacing: -10) {
Text(appName)
.font(.largeTitle)
.foregroundColor(.blue)
.padding(.bottom)
// ...
// includes a bunch of VStacks and HStacks
// ... and finally
NavigationLink(destination: SettingsView()) {
Image(systemName: "gear")
.foregroundColor(.gray)
.font(.title)
// ... more stuff
// ... and finally
}.padding(.top, -100) // NavigationView
} // body

SwiftUI Buttons in a ScrollView is not tappable sometimes

I have a horizontal ScrollView on top of a MapView.
The ScorllView is a collection of Buttons. It is weird that the buttons in the ScrollView are sometime tapable and sometimes not. First tap always works but after that I have to scroll a bit, tap around different areas in the button, make some secret prayers and then it works!
I tried disabling/removing all other components in the view, but still unable to figure out the root cause.
Has anyone experience this ?
I stuck with a same issue with horizontal ScrollView on top and List. While debugging I added empty .onTapGesture to ScrollView and it somehow fix my issue.
VStack(spacing: 0) {
ScrollView(.horizontal) {
HStack {
Button("one") {}
Button("two") {}
Button("three") {}
}
}
.onTapGesture { // <---- fix
}
List {
}
}
I also faced the same issue for Horizontal Scroll view in Swiftui dark theme "CameraTimerItem" buttons not clickable (Problem with dark theme only). Then I put a onTapGesture without any action. It's starts to work normally. I think it's a error of SwiftUI.
VStack (alignment:.center){
ScrollView(.horizontal, showsIndicators: false) {
HStack{
ForEach(timeSlots,id: \.self) { item in
CameraTimerItem(cellTitle: item)
}
}
.frame(width: AppUtils.width, alignment: .center)
}
.onTapGesture {
// <---- This is the solution
}
}
To anyone else having this issue, instead of adding an empty .onTapGesture view modifier, check that any HStacks in the ScrollView hierarchy have a .contentShape(Rectangle()) modifier. By default, HStacks don't accept taps in between their child views, and depending on your child view's layout this can cause taps to be missed even when it looks like they should be landing. .contentShape(Rectangle()) makes the entire frame of the HStack tappable.

How do I add a highlight to a TabItem label in SwiftUI

I have a TabView with custom icons - like this:
PastView( settings ).tabItem
{
Image("past")
Text("Past")
}.tag(1)
which works great - but just the text changing color is very subtle - it's not obvious which tab is "current". I want to make it much more obvious by doing something to the image. I've tried all of thse
Image("past").shadow( radius:5 )
Image("past").border( Color.red, width:8 )
Image("past").background( Color.green )
but none of them have any effect at all - and I don't really understand why
try adding ".accentColor(.red)" to the TabView.
TabItem image in SwiftUI has very limited customization at present. Even in UIKit you would be making those changes to the ImageView container not the Image itself.
Design selected and unselected versions of your icons, and then use them as below. You may also want to create different versions of the icons in the Asset Catalog for dark mode.
#State private var selectedTab = 0
var body: some View {
TabView(selection: $selectedTab) {
ViewA()
.tabItem {
selectedTab == 0 ? Image("past-selected") : Image("past-unselected")
}.tag(0)
ViewB()
.tabItem {
selectedTab == 1 ? // etc ...
}.tag(1)
}
}

How to do Apple Music-like navigation in SwiftUI? Custom List and NavigationView has highlight not going away

This is my example that I am trying to get to work:
struct ContentView: View {
let links = ["Item 1", "Item 2", "Item 3", "Item 4"]
var body: some View {
NavigationView {
ScrollView {
Text("My Title")
List(links, id: \.self) {
link in
NavigationLink(destination: TestView()) {
Text(link)
.padding(.vertical, 4)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.frame(height: 178)
Text("Some more content here")
}
}
}
}
Note: TestView is just some view with the text hello world on it.
I am trying to copy Apple Music's style of navigation. I tried putting a Button in the NavigationLink but tapping it on the text wouldn't change views, and I couldn't find a way to reliably change the color of the row when tapped, at the same time. Also in some approach, I managed to make it work, but the way the colors animate is different, i.e. it fades from A to B, over ~100ms whereas what I'm trying to achieve is to animate between the states instantly (like in Apple Music).
My current approach is using a List, putting NavigationLinks inside it and then cutting off the whole view by giving it a height. This way I can put it alongside other content.
It's working fine for now, but whenever I click on an row and go back, the row is still highlighted, when it shouldn't. Is there a way to make it so that it deselects when going back to the screen somehow?
I think this bug is being caused by the List being inside a ScrollView, since when I removed ScrollView, the list worked properly, and there wasn't this highlight bug. But I need to be able to put my content with the list, and I don't intend to have a list take up the whole screen.
Is there any way to fix this bug with this approach? I'm also willing for other ways to achieve the same result without using List.
Trying to use ForEach instead ofList?
With a view for row (CustomRow) where you can pass link item and set custom dividing line, background etc ...
ForEach(links, id: \.self) { link in
NavigationLink(destination: TestView()) {
CustomRow(item: link)
}
}
.frame(height: 178)