SwiftUI NavigationLink in list - swiftui

I tried to do a list which have image and a navigation link inside. In iOS 14.1 everything work fine but after I update my iOS to 14.2, something break. In the list while the user click the big image there will be a action sheet pop out, while the user click a systemImage it will trigger a navigation link. However, when I update to iOS 14.2, no matter what I clicked, it will trigger the NavigationLink. Can someone explain to me why will this happened and how to solve?
Here is the sample code
struct ContentView : View {
#State var showingActionSheet = false
#State private var action: Int? = 0
var body: some View {
NavigationView {
List{
VStack(alignment: .leading){
HStack{
VStack(alignment: .leading){
Text("my data")
Text("2020/12/12")
}
}
Image("profile")
.resizable()
.aspectRatio(contentMode: .fit)
.onTapGesture(count: 1) {
self.showingActionSheet.toggle()
}
HStack{
Image(systemName: "message")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25)
.foregroundColor(.gray)
.onTapGesture {
self.action = 1
print("select comment")
}
NavigationLink(destination: Text("test"), tag: 1, selection: $action) {
EmptyView()
}
}
}
.actionSheet(isPresented: $showingActionSheet) {
//action sheet
ActionSheet(title: Text("Test"), message: Text("Select a selection"), buttons: [
.default(Text("test")) { print("test") },
.cancel()
])
}
}
}
}
}

Try the following (disabling navigation link prevents user interaction on it, but programmatically it is activated):
HStack{
Image(systemName: "message")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25)
.foregroundColor(.gray)
.onTapGesture {
self.action = 1
print("select comment")
}
NavigationLink(destination: Text("test"), tag: 1, selection: $action) {
EmptyView()
}.disabled(true) // << here !!
}

You can use isActive to trigger the navigation link.
#State var isCommentPresented = false
...
Image(systemName: "message")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25)
.foregroundColor(.gray)
.onTapGesture {
self.isCommentPresented = true
print("select comment")
}
}
NavigationLink(destination: Text("test"), isActive:self.$isCommentPresented) {
EmptyView()
}

Using Zstack on top of all can be the reason.
Please check once without ZStack

Related

SwiftUI - Sheet doesn't work in Navigation View

Having issues with a NavigationView and Sheet.
I want to use not full-size sheet but bottom sheet and connect LoginView() and SignView() through sheet. At this time, frame of SignView never follow sheet.
So I tried two ways to solve.
First, LoginView: Has NavigationView out of the sheet and NavigationLink in sheet . But it didn't work.
So I put NavigationView in sheet, it works. But the height of the destination View becomes like sheet.
How can I solve the problem proper way? Thanks!
import SwiftUI
struct LoginView:View{
#State var isPlus : Bool = false
var body: some View{
NavigationView{
VStack(alignment:.center){
Spacer()
ZStack{
Button(action:{
self.isPlus = true})
{
Text("Sign up")
.padding(.horizontal,23)
.padding(20)
.font(.system(size: 25))
.fontWeight(.heavy)
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(10)
}
.sheet(isPresented: $isPlus){
VStack{
NavigationLink(destination:SignView()){
Text("MyCard")
.font(.title)
.foregroundColor(Color.black)
}
}
.presentationDetents([.height(300)])
}
}
}
}
}
}
Hope you can get solution from the below code snippet.
By this way we can use sheet in the NavigationView.
struct LoginView: View {
#State var isPlus : Bool = false
#State private var showingSheet = false
var body: some View {
NavigationView {
VStack(alignment:.center) {
Spacer()
ZStack{
Button(action:{
self.isPlus = true})
{
Text("First Sheet View")
.padding(.horizontal,23)
.padding(20)
.font(.system(size: 25))
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(10)
}
.sheet(isPresented: $isPlus){
VStack{
NavigationView {
NavigationLink(destination:SignView()){
Button(action:{
self.showingSheet = true})
{
Text("Full Sheet View")
.padding(.horizontal,23)
.padding(20)
.font(.system(size: 25))
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(10)
}
.sheet(isPresented: $showingSheet){
Button("Close"){
showingSheet = false
isPlus = false
}
.presentationDetents([.large])
}
}
}
.presentationDetents([.medium])
.edgesIgnoringSafeArea(.all)
}
}
}
}
}
}
}

Is it possible to replace the default back button and sidebar icon in iPad's Navigation Bar using NavigationView in SwiftUI?

I'm trying to make an iOS app that uses SwiftUI's NavigationView to build a side menu.
On iPhone it works perfectly, but on iPad I cannot get Rid of the Sidebar button and the back button text and icon. I would like to replace those buttons with the three horizontal lines icon named line.horizontal.3
Landscape screenshot with side menu icon I'd like to replace with three lines icon
Portrait screenshot with back button (woth icon and text) I'd like to replace with three lines icon
The code I'm using is the following:
import SwiftUI
struct ContentView_iPad: View {
#State var showMenu: Bool = false
var body: some View {
NavigationView{
MenuView()
iPad_menu()
.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading:
(Button(action: {
withAnimation{
self.showMenu.toggle()
}
}){
Image(systemName: "line.horizontal.3").imageScale(.large)
}
))
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
The MainView:
import SwiftUI
struct iPad_menu: View {
var body: some View {
EmptyView()
.background(Color.black)
}
}
The MenuView:
import SwiftUI
struct MenuView: View {
var body: some View {
VStack(alignment: .leading) {
HStack {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Profile")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 100)
HStack {
Image(systemName: "envelope")
.foregroundColor(.gray)
.imageScale(.large)
Text("Messages")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 30)
HStack {
Image(systemName: "gear")
.foregroundColor(.gray)
.imageScale(.large)
Text("Settings")
.foregroundColor(.gray)
.font(.headline)
}
.padding(.top, 30)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
}

How Can I Handle Tap Gesture on Widget?

I'm writing a widget with WidgetKit and I want to make the widget's content is clickable. For example, if users click to standings, I want to open the standings tab when the app becomes active.
I tried to use notification between the app and widget but the tap gesture is not working, I added print inside of the tap gesture but it did not appear in the console. Also, I added the same app group to both of them.
WidgetView:
struct LargeWidget : View {
#State var standings : [StandingsTable]
var body: some View {
VStack(alignment:.leading){
if standings.count > 0{
HStack(spacing:5){
Text("#").foregroundColor(.gray)
.frame(width: 30)
Text("Team".localized).foregroundColor(.gray)
Spacer()
Text("_D".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_L".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_W".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_P".localized).foregroundColor(.gray)
.frame(width: 30)
}
Divider()
ForEach(0..<5, id: \.self) { i in
HStack(spacing:5){
Text(standings[i].rank)
.font(.system(size: 15))
.padding(.vertical, 3)
.frame(width: 30)
.background(Color(UIColor.systemBackground))
.cornerRadius(4)
Text(standings[i].name)
.lineLimit(1)
.padding(.leading, 5)
Spacer()
Text(standings[i].drawn)
.frame(width: 30)
Text(standings[i].lost)
.frame(width: 30)
Text(standings[i].won)
.frame(width: 30)
Text(standings[i].points)
.frame(width: 30)
}
.padding(.vertical, 5)
.background(standings[i].name == "Besiktas" ? Color(UIColor.systemGray6) : Color.clear)
.cornerRadius(8)
}
Spacer(minLength: 0)
}else{
Text("Large")
.padding()
}
}.padding()
.onTapGesture {
print("clicked to standings")
DispatchQueue.main.async(execute: {
NotificationCenter.default.post(name: NSNotification.Name("standings"), object: nil, userInfo: nil)
})
}
}
}
and here ContentView in app:
import SwiftUI
extension NSNotification {
static let openStandings = NSNotification.Name.init("standings")
}
struct ContentView: View {
#State var show: Bool = false
var body: some View {
NavigationView{
Text("Hello, world!")
.padding()
}.sheet(isPresented: self.$show) {
VStack{
Text("Notification")
.padding()
}
}
.onReceive(NotificationCenter.default.publisher(for: NSNotification.openStandings))
{ obj in
self.show.toggle()
}
}
}
Screenshot of Widget
Okay I created a new project with SwiftUI Environments (not old way AppDelegate and SceneDelegate).
Then I use Link for tap actions and I got it with .onOpenURL modifier in ContentView. It works :)
ContentView:
import SwiftUI
enum SelectedTab: Hashable {
case home
case standings
case options
}
struct ContentView: View {
#State var selectedTab: SelectedTab = .home
var body: some View {
TabView(selection: self.$selectedTab) {
Text("Home")
.tabItem {
Image(systemName: "house")
.renderingMode(.template)
Text("Home")
}.tag(SelectedTab.home)
Text("Standings")
.tabItem {
Image(systemName: "list.number")
.renderingMode(.template)
Text("Standings")
}.tag(SelectedTab.standings)
Text("Options")
.tabItem {
Image(systemName: "gear")
.renderingMode(.template)
Text("Options")
}.tag(SelectedTab.options)
.onOpenURL { (url) in
if url.absoluteString == "widget-deeplink://standings"{
self.selectedTab = .standings
}
}
}
}
}
Link usage example in Widget:
Link(destination: URL(string: "widget-deeplink://standings")!) {
Text("Link Test")
}

Navigate to new screen on button click SwiftUI

I have a login screen and wish to navigate to a new screen when the Login button is clicked.
I did try to wrap the button and the entire screen layout under NavigationView and embedded the button in a Navigation Link.
I am unable to figure out how to show the new screen when the button is clicked. Following is the code for the login screen.
ZStack {
Color.red
.edgesIgnoringSafeArea(.all)
VStack(alignment: .center, spacing: 180.0) {
Text("SwiftUI")
.font(.largeTitle)
.bold()
.padding()
VStack(alignment: .center, spacing: 25) {
TextField("Username", text: $userName)
.padding(.all)
.background(Color.white)
.cornerRadius(10)
TextField("Password", text: $userPassword)
.padding(.all)
.background(Color.white)
.cornerRadius(10)
Toggle(isOn: $isFirstTimeUser) {
Text("First Time User")
.font(.headline)
.bold()
.padding(.horizontal, -10)
.foregroundColor(Color.white)
}.padding(.horizontal, 17)
Button(action: {
if self.userName.count <= 5 {
self.isAlertShown = true
} else {
}
})
{
Text(isFirstTimeUser ? "SignUp" : "Login")
.fontWeight(.medium)
.font(.title)
.foregroundColor(Color.red)
.padding(.horizontal, 10)
}.padding()
.background(Color.white)
.cornerRadius(10)
.alert(isPresented: $isAlertShown) {
() -> Alert in
Alert(title: Text("UserName Invalid"), message: Text("Username has to be more than 5 characters"), dismissButton:.default(Text("Got that!")))
}
}.padding(.horizontal, 17)
}
}
Here is possible approach
1) Add link tag state variable to your View
#State private var current: Int? = nil
2) Wrap your view hierarchy into NavigationView to make possible NavigationLink to work
3) Add tagged NavigationLink above your button
NavigationLink(destination: YourDestinationViewHere(), tag: 1, selection: $current) {
EmptyView()
}
4) Add link tag selection to button action
Button(action: {
if self.userName.count <= 5 {
self.isAlertShown = true
} else {
self.current = 1 // this activates NavigationLink with specified tag
}
})

Button appears on top but not pressable

public struct Frontside: View
{
#Binding public var kanatext: String
public var body: some View
{
ZStack{
RoundedRectangle(cornerRadius: 25, style: .continuous)
.foregroundColor(Color.red)
.frame(width: 160, height: 160)
.zIndex(0)
Text(self.kanatext)
.font(.title)
.fontWeight(.black)
.padding(50)
.zIndex(1)
VStack {
Spacer()
HStack {
Button(action: {
print("button pressed")
}) {
Image(systemName: "xmark.circle")
.font(.title)
}
.mask(Circle())
.opacity(0.4)
Button(action: {
print("button pressed")
}) {
Image(systemName: "checkmark.circle")
.font(.title)
}
.mask(Circle())
.opacity(0.4)
}
}
.zIndex(2)
}
}
}
In the above code snippet, I have used a Zstack to layer different parts of a flash card, a background, the text, and then correct/incorrect buttons. The uppermost layer are the buttons, which appear correctly, but for some reason they are not actually pressable.
Have you tried using onTapGesture? It might have to do with the fact that you're using a ZStack.
Try something like:
Image(systemName: "checkmark.circle")
.font(.title)
.onTapGesture {
print("button pressed")
}
In this case there would be no needed to wrap the Image in a Button.
If this doesn't work add onTapGesture to your VStack and have it be empty.