tvOS Button inside NavigationLink is not Working - swift

I have built an iOS app using swift and swiftui and now I am working on the tvOS version of the app. However, I have a couple of issues. One of the issues is still unresolved: tvOS Textfield Transparent Background
I was creating a login page and I realized that the button inside the NavigationLink was not doing anything. Then to test and identify the issue I created a simple button as follows:
Button(action: {
print("Login pressed")
}) {
Text("Login")
.frame(width:300, height: 35)
}
.background(Color.blue)
.foregroundColor(Color.white)
Output when clicked on:
Login pressed
And it looks like this:
And it looks like this when focused:
But when I insert that button inside a NavigationLink, Navigation like acts like a button and this button does nothing.
The code is like that:
NavigationLink(destination: mainScene(), tag:1, selection: $selection) {
Button(action: {
print("Login pressed")
self.selection = 1
}) {
Text("Login")
.frame(width:300, height: 35)
}
.background(Color.blue)
.foregroundColor(Color.white)
}
In the iOS, I have a similar code, and it does not navigate until everything in the button action is executed and selection is equal to 1. But in this, it does not execute anything and it just directly navigates to the next page. The button looks like this when I embed it in the NavigationLink:
As you can see there is white space around the original login button and the focus is on that white space. And when clicked on it navigates. It looks like the NavigationLink acts like a button but it also prevents the button action to be executed. I am having these kind of problems that are mainly caused by the focus. For Example in the images above, as you can see the shape and the color of the button changes when the focus is on but I want to control it. However, I don't know how I can play with on focus design of the items.

Try the following
Button(action: {
print("Login pressed")
self.selection = 1
}) {
Text("Login")
.frame(width:300, height: 35)
}
.background(Color.blue)
.foregroundColor(Color.white)
.background(NavigationLink(destination: mainScene(), tag:1,
selection: $selection) { EmptyView() })

Try This:
NavigationLink(destination: mainScene(), tag:1, selection: $selection) {
Button(action: {
print("Login pressed")
self.selection = 1
}) {
Text("Login")
.frame(width:300, height: 35)
}
.background(Color.blue)
.foregroundColor(Color.white)
}.buttonStyle(PlainButtonStyle())

Related

SwiftUI Button cannot be clicked on its whole area

I built a custom Button.
My problem is, that it can be only clicked on the Text Elements not on the whole area of the Button.
What's going wrong, how can I solve this?
struct MyButton: View {
var body: some View
{ Button( action:{ print("pressed") })
{ HStack {
VStack(alignment: .leading){
Text("Tomorrow").font(.caption)
Text("23.5.22 KW 23")
}
Spacer()
}
}
.padding([.horizontal],4.0)
.padding([.vertical],2.0)
.background(RoundedRectangle(cornerRadius: 5).fill(Color.red))
.buttonStyle(PlainButtonStyle())
.padding([.horizontal],8.0)
}
}
By default, only the parts of the view that actually render something are tappable. You need to add a .contentShape(Rectangle()) modifier to your Button to make the entire area interactive.

Move to nextView on button click based on condition, swiftUI

I've read a lot here about navigation in SwiftUI and tried a couple of things, but nothing is working as desired.
I am developing a watch app in which I have to use two TextField. I only want to move on next screen when both field are not empty. Here is my code.
#State var movetoNextScreen: Int? = nil
NavigationLink(destination: WaterTemprature(), tag: 1,
selection: $movetoNextScreen) {
ZStack {
Text("Next")
.font(.system(size: 12, weight: .semibold))
.frame(alignment: .center)
Button(action: nextBtnPressed) {
if !check_minute_and_seconds_are_empty() {
// move to next screen.
movetoNextScreen = 1
}
}.opacity(0)
}
}
But when I pressed the navigation link, Button action did not call and control move to next screen. So I want to find any method that can first check the textfields and then move to next view.
Try in opposite direction - put button in front (with needed redesign) and place link in background with programmatic activation, like
Button(action: {
if !check_minute_and_seconds_are_empty() {
movetoNextScreen = 1 // << activate link
}
}) {
Text("Next")
.font(.system(size: 12, weight: .semibold))
.frame(alignment: .center)
}
.background(
NavigationLink(destination: WaterTemprature(), tag: 1,
selection: $movetoNextScreen) { EmptyView() }
)

Swift Navigation Link problems

I am having problems with NavigationLink in Swift and iOS 14. This is a known issue, however the problem solvers that I saw, are not sufficient. I found out that when a button with a NavigationLink is initialized early, it works and when you enter the page, you have the "go back" button. However, when the navigation link is in the middle, you are not able to go back when you enter the page and must close the app to go to the main screen again.
This is the code I am using:
HStack {
Button(action: {
if let url = URL(string: "I blacked this out to make no advert here") {
UIApplication.shared.open(url)
}
}){
Text("Rate App").foregroundColor(.gray)
}.modifier(navigationButtonViewStyle())
Spacer()
// this navigation link works fine
NavigationLink(destination: HelpView()) {
Text("FAQ").foregroundColor(.gray)
}.modifier(navigationButtonViewStyle())
}.padding(.horizontal, 20)
VStack {
Text("λSET")
.font(.largeTitle)
.fontWeight(.heavy)
.bold()
.padding(.top, 50)
.onAppear(perform: prepareHaptics)
.animation(.linear)
Text("λudio System Engineer Tools")
.fontWeight(.light)
.animation(.linear(duration: 0.5))
HStack {
Spacer()
// this navigation link is not showing the go back button when pressed and directed to the new screen
NavigationLink(destination: AboutView()) {
Text("About λSET")
.fontWeight(.heavy)
.bold()
}
Spacer()
}.modifier(calcTitleViewStyle())
.padding(.top, 20)
.padding(.bottom, 60)
}
I am not sure what the problem is, but am curious what this could be. I am working on a workaround, but I would love to solve the problem at its roots.
Thanks!
Fixed it myself by explicitly setting the .NavigationBar to true to force swift to load it. This was not needed in other pages, but I am now doing to make sure IOS14 accepts it, since a few things have been changed there.

Creating a navbar with 3 items

I'm trying to create something like this:
A navigation bar with 3 items, is it possible to do this using navigationBarItems?
My current plan is to hide the navbar using:
.navigationBarTitle("")
.navigationBarHidden(true)
and then creating the 3 buttons using a HStack. The Problem I have is because I'm hiding the navbar, the click of one of the buttons take it to another view, which also then hides the navbar (Thats not what im looking for)
I have tried:
.navigationBarItems(trailing:
HStack {
Button("About") {
print("About tapped!")
}
Button("Help") {
print("Help tapped!")
}
}
)
But this creates the two items next to each other on the right side. I tried putting a Spacer() in the above HStack, but this doesn't work.
I would prefer to use navigationBarItems but can't seem to find a way to centre an item?
A navigation bar with 3 items, is it possible to do this using navigationBarItems?
No. Moreover navigationBarItems modifier is deprecated since SwiftUI 2.0
SwiftUI 2.0
This can be done with toolbar modifier as easy as attach it to any view inside NavigationView
Demo prepared & tested with Xcode 12 / iOS 14:
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: {}) { Image(systemName: "gear") }
}
ToolbarItem(placement: .principal) {
Button(action: {}) { Image(systemName: "car") }
}
ToolbarItem(placement: .navigation) {
Button(action: {}) { Image(systemName: "chevron.left") }
}

SwiftUI: Two buttons with the same width/height

I have 2 buttons in an H/VStack. Both of them contain some text, in my example "Play" and "Pause". I would like to have that both buttons have the same width (and height) determined by the largest button. I have found some answers right here at SO but I can't get this code working unfortunately.
The following code illustrates the question:
import SwiftUI
struct ButtonsView: View {
var body: some View {
VStack {
Button(action: { print("PLAY tapped") }){
Text("Play")
}
Button(action: { print("PAUSE tapped") }) {
Text("Pause")
}
}
}
}
struct ButtonsView_Previews: PreviewProvider {
static var previews: some View {
ButtonsView()
}
}
The tvOS preview from Xcode shows the problem:
I would be thankful for an explanation for newbies 🙂
Here is run-time based approach without hard-coding. The idea is to detect max width of available buttons during drawing and apply it to other buttons on next update cycle (anyway it appears fluently and invisible for user).
Tested with Xcode 11.4 / tvOS 13.4
Required: Simulator or Device for testing, due to used run-time dispatched update
struct ButtonsView: View {
#State private var maxWidth: CGFloat = .zero
var body: some View {
VStack {
Button(action: { print("PLAY tapped") }){
Text("Play")
.background(rectReader($maxWidth))
.frame(minWidth: maxWidth)
}.id(maxWidth) // !! to rebuild button (tvOS specific)
Button(action: { print("PAUSE tapped") }) {
Text("Pause Long Demo")
.background(rectReader($maxWidth))
.frame(minWidth: maxWidth)
}.id(maxWidth) // !! to rebuild button (tvOS specific)
}
}
// helper reader of view intrinsic width
private func rectReader(_ binding: Binding<CGFloat>) -> some View {
return GeometryReader { gp -> Color in
DispatchQueue.main.async {
binding.wrappedValue = max(binding.wrappedValue, gp.frame(in: .local).width)
}
return Color.clear
}
}
}
You can implement the second custom layout example in the WWDC 2022 talk https://developer.apple.com/videos/play/wwdc2022/10056/ titled "Compose custom layouts with SwiftUI" which, if I understand the question, specifically solves it, for an arbitrary number of buttons/subviews. The example starts at the 7:50 mark.
after reading hit and trial implementing SO solns etc finally resolved this issue posting so that newbies as well as intermediate can benefit
paste it and obtain equal size(square) views
VStack(alignment: .center){
HStack(alignment:.center,spacing:0)
{
Button(action: {}, label: {
Text("Button one")
.padding(35)
.foregroundColor(.white)
.font(.system(size: 12))
.background(Color.green)
.frame(maxWidth:.infinity,maxHeight: .infinity)
.multilineTextAlignment(.center)
.cornerRadius(6)
}).background(Color.green)
.cornerRadius(6)
.padding()
Button(action: {}, label: {
Text("Button two")
.padding(35)
.foregroundColor(.white)
.font(.system(size: 12))
.frame(maxWidth:.infinity,maxHeight: .infinity)
.background(Color.green)
.multilineTextAlignment(.center)
}) .background(Color.green)
.buttonBorderShape(.roundedRectangle(radius: 8))
.cornerRadius(6)
.padding()
}.fixedSize(horizontal: false, vertical: true)
Add as many as buttons inside it. You can adjust it for VStack by adding only one button in hstack and add another button in another Hstack. I gave a general soln for both VStack and Hstack. You can also adjust padding of button as .padding(.leading,5) .padding(.top,5) .padding(.bottom,5) .padding(.trailing,5) to adjust the gaps between buttons
I think the best solution is to use GeometryReader, which resizes the width of the content of the Button. However, you need to check that you set a width of the Wrapper around the GeometryReader, because otherwise it would try to use the full screen width. (depends where you use that view, or if it is your primary view)
VStack
{
GeometryReader { geo in
VStack
{
Button(action: { print("PLAY tapped") }){
Text("Play")
.frame(width: geo.size.width)
}
.border(Color.blue)
Button(action: { print("Pause tapped") }){
Text("PAUSE")
.frame(width: geo.size.width)
}
.border(Color.blue)
}
}
}
.frame(width: 100)
.border(Color.yellow)
... which will look like that.
What happens if you put a Spacer() right after the Text("Play")? I think that might stretch out the 'Play' button.
Or maybe before and after Text("Play").