#Environment(\.presentationMode) var mode: Binding<PresentationMode> Messing other views - swift

I have a MailView()
import Foundation
import SwiftUI
import UIKit
import MessageUI
struct MailView: UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentation
#Binding var result: Result<MFMailComposeResult, Error>?
let newSubject : String
let newMsgBody : String
class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
#Binding var presentation: PresentationMode
#Binding var result: Result<MFMailComposeResult, Error>?
init(presentation: Binding<PresentationMode>,
result: Binding<Result<MFMailComposeResult, Error>?>) {
_presentation = presentation
_result = result
}
func mailComposeController(_ controller: MFMailComposeViewController,
didFinishWith result: MFMailComposeResult,
error: Error?) {
defer {
$presentation.wrappedValue.dismiss()
}
guard error == nil else {
self.result = .failure(error!)
return
}
self.result = .success(result)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(presentation: presentation,
result: $result)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {
let vc = MFMailComposeViewController()
vc.mailComposeDelegate = context.coordinator
vc.setToRecipients(["hello#email.co.uk"])
vc.setSubject(newSubject)
vc.setMessageBody(newMsgBody, isHTML: false)
return vc
}
func updateUIViewController(_ uiViewController: MFMailComposeViewController,
context: UIViewControllerRepresentableContext<MailView>) {
}
}
And In my SettingViews its called like so:
import SwiftUI
import URLImage
import UIKit
import MessageUI
struct SettingsView: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#State private var showMailSheet = false
#State var result: Result<MFMailComposeResult, Error>? = nil
#State private var subject: String = ""
#State private var emailBody: String = ""
#EnvironmentObject var session: SessionStore
var body: some View {
NavigationView {
VStack(alignment: .leading) {
List {
Section(header: Text("Account")) {
NavigationLink(destination: ProfileView()) {
HStack {
Image(systemName: "person")
.resizable()
.frame(width: 20, height: 20)
VStack(alignment: .leading) {
Text("Edit Profile").font(.callout).fontWeight(.medium)
}
}.padding([.top,.bottom],5).padding(.trailing,10)
}
NavigationLink(destination: AccountView()) {
HStack {
Image(systemName: "doc")
.resizable()
.frame(width: 20, height: 20)
VStack(alignment: .leading) {
Text("View Account").font(.callout).fontWeight(.medium)
}
}.padding([.top,.bottom],5).padding(.trailing,10)
}
NavigationLink(destination: PreferencesView()) {
HStack {
Image(systemName: "slider.horizontal.3")
.resizable()
.frame(width: 20, height: 20)
VStack(alignment: .leading) {
Text("Preferences").font(.callout).fontWeight(.medium)
}
}.padding([.top,.bottom],5).padding(.trailing,10)
}
}
Section(header: Text("Support")) {
HStack {
Image(systemName: "bubble.right")
.resizable()
.frame(width: 20, height: 20)
VStack(alignment: .leading) {
Text("Contact Us").font(.callout).fontWeight(.medium)
}
Spacer()
Button(action: {
self.subject = "Hello"
self.sendEmail()
}) {
Text("Send").font(.system(size:12))
}
}
HStack {
Image(systemName: "ant")
.resizable()
.frame(width: 20, height: 20)
VStack(alignment: .leading) {
Text("Report An Issue").font(.callout).fontWeight(.medium)
}
Spacer()
Button(action: {
self.sendEmail()
self.subject = "Report Issue"
self.emailBody = "Im having the following issues:"
}) {
Text("Report").font(.system(size:12))
}
}
}
Section (header: Text("Legal")) {
HStack {
Image(systemName: "hand.raised")
.resizable()
.frame(width: 20, height: 20)
VStack(alignment: .leading) {
Text("Privacy Policy").font(.callout).fontWeight(.medium)
}
Spacer()
Button(action: {
if let url = URL(string: "http://www.mysite.co.uk/privacy.html") {
UIApplication.shared.open(url)
}
}) {
Text("View").font(.system(size:12))
}
}
HStack {
Image(systemName: "folder")
.resizable()
.frame(width: 20, height: 20)
VStack(alignment: .leading) {
Text("Terms and Conditions (EULA)").font(.callout).fontWeight(.medium)
}
Spacer()
Button(action: {
if let url = URL(string: "http://www.mysite.co.uk/eula.html") {
UIApplication.shared.open(url)
}
}) {
Text("View").font(.system(size:12))
}
}
}
}.listStyle(GroupedListStyle())
}.navigationBarTitle("Settings", displayMode: .inline)
.background(NavigationBarConfigurator())
}.sheet(isPresented: $showMailSheet) {
MailView(result: self.$result, newSubject: self.subject, newMsgBody: self.emailBody)
}
}
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
self.showMailSheet = true
} else {
print("Error sending mail")
}
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
SettingsView()
}
}
My Sheet is appearing nicely and once the email is sent, the sheet is dismissed as expected, but the following is causing an issue:
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
When I click:
NavigationLink(destination: ProfileView()) {
HStack {
Image(systemName: "person")
.resizable()
.frame(width: 20, height: 20)
VStack(alignment: .leading) {
Text("Edit Profile").font(.callout).fontWeight(.medium)
}
}.padding([.top,.bottom],5).padding(.trailing,10)
}
There is a action sheet:
.actionSheet(isPresented: self.$profileViewModel.showActionSheet){
ActionSheet(title: Text("Add a profile image"), message: nil, buttons: [
.default(Text("Camera"), action: {
self.profileViewModel.showImagePicker = true
self.sourceType = .camera
}),
.default(Text("Photo Library"), action: {
self.profileViewModel.showImagePicker = true
self.sourceType = .photoLibrary
}),
.cancel()
])
}.sheet(isPresented: self.$profileViewModel.showImagePicker){
imagePicker(image: self.$profileViewModel.upload_image, showImagePicker: self.$profileViewModel.showImagePicker, sourceType: self.sourceType)
}
When i click this button it keeps dismissing the button and I can't click on the options presented.
Any idea how I can have the #Environment(\.presentationMode) var mode: Binding<PresentationMode> only effecting the dismissing of the email? and not interfering with anything else?

#Environment(\.presentationMode) should be used for the last child view that you want to have this custom behaviour.
Any child view from where you declared the #Environment(\.presentationMode), will also inherit the same behaviour.
If you declare it only in MailView, it should fix it.

Related

How to transition to a new view in swiftui

My goal is to have the user click a button that then gives them a choice of yes or cancel, this works fine. Is yes is selected it should move to the Camera View. I am getting the error: Result of 'NavigationLink<Label, Destination>' initializer is unused.
struct ContentView: View {
#State private var showAlert = false
var body: some View {
NavigationStack
{
VStack {
Button{
showAlert = true
} label: {
Text("+")
}
.frame(width: 40, height: 40)
.symbolVariant(.fill)
.background(.red)
.cornerRadius(15)
.foregroundColor(.white)
.padding(.trailing,300)
Spacer()
}
.alert("Create Event Here?", isPresented: $showAlert) {
Button("Yes"){NavigationLink("addCameraView", destination: CameraView())//*****gets the error
}
Button("Cancel", role: .cancel) { }
}
}
struct CameraView: View{
#State private var sourceType: UIImagePickerController.SourceType = .photoLibrary
#State private var selectedImage: UIImage?
#State private var imagePickerDisplay = false
var body: some View {
NavigationView {
VStack {
if selectedImage != nil {
Image(uiImage: selectedImage!)
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.frame(width: 300, height: 300)
} else {
Image(systemName: "snow")
.resizable()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.frame(width: 300, height: 300)
}
Button("Camera") {
self.sourceType = .camera
self.imagePickerDisplay.toggle()
}.padding()
}
.navigationBarTitle("Take a Photo of the Event")
.sheet(isPresented: self.$imagePickerDisplay) {
ImagePickerView(selectedImage: self.$selectedImage, sourceType: self.sourceType)
}
}
}
}
}
To navigate with a button, we need to utilize a variable as our trigger. Wrapping the button in a NavigationLink and updating the associated variable to the appropriate value will trigger the navigation.
Below you will find the updated ContentView. CameraView remains unchanged.
import SwiftUI
struct ContentView: View {
#State private var showAlert = false
#State var selection: Int? = nil
var body: some View {
NavigationStack
{
VStack {
Button{
showAlert = true
} label: {
Text("+")
}
.frame(width: 40, height: 40)
.symbolVariant(.fill)
.background(.red)
.cornerRadius(15)
.foregroundColor(.white)
.padding(.trailing,300)
Spacer()
}
.alert("Create Event Here?", isPresented: $showAlert) {
NavigationLink(destination: CameraView(), tag: 1, selection: $selection) {
Button("Yes"){
selection = 1
}
}
Button("Cancel", role: .cancel) {
selection = nil
}
}
}
}
}

How do I pass a var from one viewModel to a new viewModel in my .fullScreenCover() View?

I'm building an app with swiftUI & firebase/firestore. I open a fullscreenCover sheet of a product selected from a catalog and the user can add it to a list below it. The product is selected on the previous page, and that is passed onto the .fullScreeenCover, where I'm introducing a new ViewModel for the list.
Where I'm getting confused: How do I pass the product ID passed into this fullScreenCover view into my newly introduced list's viewModel so that I can run the "add to list" function?
ViewModel for my List:
import SwiftUI
import Firebase
class ListViewModel: ObservableObject {
let var product: Product
#Published var userList = [List]()
#Published var list: List
init(list: List) {
self.list = list
}
func fetchList() {
let docRef = Firestore.firestore().collection("List")
guard let uid = AuthViewModel.shared.userSession?.uid else { return }
docRef.whereField("uid", isEqualTo: uid).getDocuments { snapshot, _ in
guard let documents = snapshot?.documents else { return }
self.userList = documents.map({ List(dictionary: $0.data())} )}
}
func AddProductToList(product: Product, list: List) {
let listRef = Firestore.firestore().collection("List").document(list.id).collection("Products")
let productRef = Firestore.firestore().collection("Products").document(product.id)
productRef.getDocument { snapshot, _ in
listRef.document(self.product.id).setData([:]) { _ in
print("\(self.product.title) was saved to \(self.list.name)")
}
}
}
}
Code for .fullScreenCover() sheet View
import SwiftUI
struct ListCoverView: View {
#Binding var isPresented: Bool
let viewModel: LikeViewModel
#StateObject var listViewModel = ListViewModel(list)
var body: some View {
ZStack {
VStack (spacing: 10) {
VStack {
WebImage(url: URL(string: viewModel.product.image))
.resizable()
.frame(width: 220, height: 220)
.padding(.top,10)
Text("\(viewModel.product.title)")
.fontWeight(.bold)
.foregroundColor(.black)
.padding(.horizontal)
Text(viewModel.product.company)
.foregroundColor(.black)
.fontWeight(.bold)
}
.onAppear(perform: {
// Fetch products
listViewModel.fetchList()
})
ScrollView {
VStack {
Button(action: listViewModel.AddProductToList(Product)) {
ForEach(listViewModel.userList){list in
ListRow(list: listViewModel.list, viewModel: listViewModel)
}
}
}
}
}
}
Spacer()
Button(action: {isPresented.toggle()}, label : {
Text("Close")
.font(.system(size: 20))
.foregroundColor(.black)
})
.padding()
}
}
Parent View of the .fullScreenCover()
import SwiftUI
import SDWebImageSwiftUI
struct CardView: View {
let product: Product
#ObservedObject var viewModel: LikeViewModel
#State private var isShowingNewListSheet = false
init(product: Product) {
self.product = product
self.viewModel = LikeViewModel(product: product)
}
var body: some View {
VStack {
WebImage(url: URL(string: product.image))
.resizable()
.aspectRatio(contentMode: .fit)
Text(product.title)
.fontWeight(.bold)
.foregroundColor(.black)
.padding(.horizontal)
Text(product.company)
.foregroundColor(.black)
.fontWeight(.semibold)
.padding(.trailing,65)
HStack{
Button(action: {
viewModel.didLike ? viewModel.UnlikeProduct() : viewModel.LikeProduct()
}, label : {
Image(systemName: viewModel.didLike ? "heart.fill" : "heart")
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(viewModel.didLike ? .red : .black)
})
.padding(.trailing,5)
Button(action: { isShowingNewListSheet.toggle()
}, label : {
Image(systemName: "square.and.arrow.down")
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(.black)
})
.fullScreenCover(isPresented: $isShowingNewListSheet) {
ListCoverView(isPresented: $isShowingNewListSheet, viewModel: viewModel)
}
}
}
.padding(.bottom)
.background(Color(.white))
.cornerRadius(15)
}
}

CoreData data not showing up in my list view

I have a problem with CoreData...I've managed to get it to work, but it won't show my title property (which is the task name). I'm pretty new at this, I hope someone can help me out.
MainUI <--this is a pic of what the UI looks like and where the problem is.
This is the main ViewController
import SwiftUI
struct ContentView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: Task.entity(), sortDescriptors: []) var tasks: FetchedResults<Task>
#State var isPresented = false
var body: some View {
VStack {
NavigationView {
ZStack {
List{
ForEach(tasks, id: \.id){ task in
CellView(completionState: task.completionState, title: task.title!)
}
}
Button(action: {
self.isPresented.toggle()
print(isPresented)
}) {
CircleView()
}.offset(x: 158, y: 250)
}
.navigationBarTitle("Infinito")
.sheet(isPresented: $isPresented, content: {
EditView()
.environment(\.managedObjectContext, self.moc)
})
}
HStack(alignment: .center, spacing: 97) {
Button(action: {}) {
Image(systemName: "highlighter")
.resizable()
.frame(width: 45, height: 45)
.padding()
.foregroundColor(.orange)
}.offset(x: 10)
Button(action: {}){
Image(systemName: "timelapse")
.resizable()
.frame(width: 45, height: 45)
.offset(x: 4, y: 0)
.padding()
.foregroundColor(.green)
}.offset(x: -7)
.padding(.trailing, 20.0)
Button(action: {}){
Image(systemName: "alarm")
.resizable()
.frame(width: 45, height: 45)
.foregroundColor(.red)
}.offset(x: -24)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return ContentView().environment(\.managedObjectContext, context)
}
}
}
struct CellView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: Task.entity(), sortDescriptors: []) var tasks: FetchedResults<Task>
var completionState: Bool
var title: String
var body: some View {
HStack{
switch completionState{
case false:
Image(systemName: "square")
case true:
Image(systemName: "checkmark.square")
.foregroundColor(.green)
Text(title)
.foregroundColor(.black)
}
}
}
}
And this is the code for the sheet view
//
// EditView.swift
// Infinito
//
// Created by Armando Visini on 28/10/2020.
//
import SwiftUI
struct EditView: View{
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: Task.entity(), sortDescriptors: []) var tasks: FetchedResults<Task>
#State var titleOfTask: String = ""
var body: some View {
NavigationView {
VStack(alignment: .leading){
Text("Title of task")
.font(.title)
.fontWeight(.bold)
HStack {
TextField("Enter name of task here...", text: $titleOfTask)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
let task = Task(context: self.moc)
task.id = UUID()
task.title = titleOfTask
task.completionState = false
try? self.moc.save()
UIApplication.shared.endEditing()
}) {
Text("Confirm")
.foregroundColor(.white)
.fontWeight(.medium)
.background(Color(.blue))
.cornerRadius(6.0)
}
}
}
.padding()
.offset(y: -200.0)
}
}
struct EditView_Previews: PreviewProvider {
static var previews: some View {
EditView()
}
}
}
extension UIApplication{
func endEditing() {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
//
// EditView.swift
// Infinito
//
// Created by Armando Visini on 28/10/2020.
//
import SwiftUI
struct EditView: View{
#Environment(\.managedObjectContext) var moc
#FetchRequest(entity: Task.entity(), sortDescriptors: []) var tasks: FetchedResults<Task>
#State var titleOfTask: String = ""
var body: some View {
NavigationView {
VStack(alignment: .leading){
Text("Title of task")
.font(.title)
.fontWeight(.bold)
HStack {
TextField("Enter name of task here...", text: $titleOfTask)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
let task = Task(context: self.moc)
task.id = UUID()
task.title = titleOfTask
task.completionState = false
try? self.moc.save()
UIApplication.shared.endEditing()
}) {
Text("Confirm")
.foregroundColor(.white)
.fontWeight(.medium)
.background(Color(.blue))
.cornerRadius(6.0)
}
}
}
.padding()
.offset(y: -200.0)
}
}
struct EditView_Previews: PreviewProvider {
static var previews: some View {
EditView()
}
}
}
extension UIApplication{
func endEditing() {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
It looks like inside your CellView your not closing the switch statement, this will probably cause that your Text is only displayed when the checkbox ist checked. (completionState is true) Place your Text that contains your title outside the switch statement:
HStack {
switch completionState {
case false:
Image(systemName: "square")
case true:
Image(systemName: "checkmark.square")
.foregroundColor(.green)
}
Text(title)
.foregroundColor(.black)
}

SwiftUI TextField Bug

I'm having trouble with a bug in the TextField Keyboard.
When I tap the corresponding textField, the Keyboard appears, but something like a white View appears together and the TextField is hidden. (See image)
Xcode12 Iphone11-Ios13.5 Simulator doesn't have this bug, but Ios14 does. Does anyone know a solution?
struct PlaceholderTextField: View {
var placeholderTxt: String
var keyboardType: UIKeyboardType?
#Binding var text: String
var body: some View {
ZStack(alignment: .trailing) {
VStack(alignment: .leading) {
VStack {
if self.keyboardType != nil {
TextField(self.placeholderTxt, text: $text)
.autocapitalization(.none)
.padding(20)
.keyboardType(self.keyboardType!)
} else {
TextField(self.placeholderTxt, text: $text)
.autocapitalization(.none)
.padding(20)
}
}
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding()
}
}
}
}
struct LoginView: View {
#ObservedObject(initialValue: LoginViewModel()) var loginController: LoginViewModel
#EnvironmentObject var userData: UserData
#State var emailLogin: Bool = true
#Environment(\.presentationMode) var presentation
#Binding var rootIsActive: Bool
var body: some View {
ZStack {
GeometryReader { bodyView in
ZStack {
Color.backgroundColor.edgesIgnoringSafeArea(.all)
VStack(spacing: 0) {
SwitchAccountIDButton(emailLogin: self.$emailLogin)
self.userIdTextField
self.passwordTextField
Button(action: {
self.userData.isLoading = true
if self.emailLogin {
self.loginController.singin(self.loginController.email, self.loginController.password, self.emailLogin)
} else {
self.loginController.singin(self.loginController.phoneNumber, self.loginController.password, self.emailLogin)
}
}) {
ButtonView(title: "ログイン", fontColor: .white, bgColor: Color.primaryColor, width: bodyView.size.width * 0.9)
.accessibility(identifier: "login_login_button")
}
NavigationLink(destination: ReissuePassword(shouldPopToRootView: self.$rootIsActive)) {
Text("パスワードを忘れた場合")
.underline()
.foregroundColor(Color.primaryColor)
.padding(.top)
}.isDetailLink(false)
Spacer()
}
}
}
.navigationBarTitle("ログイン")
.navigationBarBackButtonHidden(true)
.navigationBarItems(trailing: VStack {
Button(action: {
self.presentation.wrappedValue.dismiss()
}, label: { Text("キャンセル").foregroundColor(Color.primaryColor).fontWeight(.regular) })
})
}
}
var userIdTextField: some View {
VStack {
if self.emailLogin {
PlaceholderTextField(placeholderTxt: "メールアドレス",keyboardType: .default ,text: self.$loginController.email)
.accessibility(identifier: "login_mailaddress_textfield")
} else {
PlaceholderTextField(placeholderTxt: "電話番号", keyboardType: .phonePad, text: self.$loginController.phoneNumber)
.accessibility(identifier: "login_phonenumber_textfield")
}
}
}
}

Tabbar middle button utility function in SwiftUI

I'm trying to reproduce a "Instagram" like tabBar which has a "Utility" button in the middle which doesn't necessarily belong to the tabBar eco system.
I have attached this gif to show the behaviour I am after. To describe the issue. The tab bar in the middle (Black plus) is click a ActionSheet is presented INSTEAD of switching the view.
How I would do this in UIKit is simply use the
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
print("Selected item")
}
Function from the UITabBarDelegate. But obviously we can't do this in SwiftUI so was looking to see if there was any ideas people have tried. My last thought would be to simply wrap it in a UIView and use it with SwiftUI but would like to avoid this and keep it native.
I have seen a write up in a custom TabBar but would like to use the TabBar provided by Apple to avoid any future discrepancies.
Thanks!
Edit: Make the question clearer.
Thanks to Aleskey for the great answer (Marked as correct). I evolved it a little bit in addition to a medium article that was written around a Modal. I found it to be a little different
Here's the jist.
A MainTabBarData which is an Observable Object
final class MainTabBarData: ObservableObject {
/// This is the index of the item that fires a custom action
let customActiontemindex: Int
let objectWillChange = PassthroughSubject<MainTabBarData, Never>()
var previousItem: Int
var itemSelected: Int {
didSet {
if itemSelected == customActiontemindex {
previousItem = oldValue
itemSelected = oldValue
isCustomItemSelected = true
}
objectWillChange.send(self)
}
}
func reset() {
itemSelected = previousItem
objectWillChange.send(self)
}
/// This is true when the user has selected the Item with the custom action
var isCustomItemSelected: Bool = false
init(initialIndex: Int = 1, customItemIndex: Int) {
self.customActiontemindex = customItemIndex
self.itemSelected = initialIndex
self.previousItem = initialIndex
}
}
And this is the TabbedView
struct TabbedView: View {
#ObservedObject private var tabData = MainTabBarData(initialIndex: 1, customItemIndex: 2)
var body: some View {
TabView(selection: $tabData.itemSelected) {
Text("First Screen")
.tabItem {
VStack {
Image(systemName: "globe")
.font(.system(size: 22))
Text("Profile")
}
}.tag(1)
Text("Second Screen")
.tabItem {
VStack {
Image(systemName: "plus.circle")
.font(.system(size: 22))
Text("Profile")
}
}.tag(2)
Text("Third Screen")
.tabItem {
VStack {
Image(systemName: "number")
.font(.system(size: 22))
Text("Profile")
}
}.tag(3)
}.actionSheet(isPresented: $tabData.isCustomItemSelected) {
ActionSheet(title: Text("SwiftUI ActionSheet"), message: Text("Action Sheet Example"),
buttons: [
.default(Text("Option 1"), action: option1),
.default(Text("Option 2"), action: option2),
.cancel(cancel)
]
)
}
}
func option1() {
tabData.reset()
// ...
}
func option2() {
tabData.reset()
// ...
}
func cancel() {
tabData.reset()
}
}
struct TabbedView_Previews: PreviewProvider {
static var previews: some View {
TabbedView()
}
}
Similar concept, just uses the power of SwiftUI and Combine.
You could introduce new #State property for storing old tag of presented tab. And perform the next method for each of your tabs .onAppear { self.oldSelectedItem = self.selectedItem } except the middle tab. The middle tab will be responsible for showing the action sheet and its method will look the following:
.onAppear {
self.shouldShowActionSheet.toggle()
self.selectedItem = self.oldSelectedItem
}
Working example:
import SwiftUI
struct ContentView: View {
#State private var selectedItem = 1
#State private var shouldShowActionSheet = false
#State private var oldSelectedItem = 1
var body: some View {
TabView (selection: $selectedItem) {
Text("Home")
.tabItem { Image(systemName: "house") }
.tag(1)
.onAppear { self.oldSelectedItem = self.selectedItem }
Text("Search")
.tabItem { Image(systemName: "magnifyingglass") }
.tag(2)
.onAppear { self.oldSelectedItem = self.selectedItem }
Text("Add")
.tabItem { Image(systemName: "plus.circle") }
.tag(3)
.onAppear {
self.shouldShowActionSheet.toggle()
self.selectedItem = self.oldSelectedItem
}
Text("Heart")
.tabItem { Image(systemName: "heart") }
.tag(4)
.onAppear { self.oldSelectedItem = self.selectedItem }
Text("Profile")
.tabItem { Image(systemName: "person.crop.circle") }
.tag(5)
.onAppear { self.oldSelectedItem = self.selectedItem }
}
.actionSheet(isPresented: $shouldShowActionSheet) { ActionSheet(title: Text("Title"), message: Text("Message"), buttons: [.default(Text("Option 1"), action: option1), .default(Text("Option 2"), action: option2) , .cancel()]) }
}
func option1() {
// do logic 1
}
func option2() {
// do logic 2
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Previous answers did not help me so I'm pasting my complete solution.
import SwiftUI
import UIKit
enum Tab {
case map
case recorded
}
#main
struct MyApp: App {
#State private var selectedTab: Tab = .map
#Environment(\.scenePhase) private var phase
var body: some Scene {
WindowGroup {
VStack {
switch selectedTab {
case .map:
NavigationView {
FirstView()
}
case .recorded:
NavigationView {
SecondView()
}
}
CustomTabView(selectedTab: $selectedTab)
.frame(height: 50)
}
}
}
}
struct FirstView: View {
var body: some View {
Color(.systemGray6)
.ignoresSafeArea()
.navigationTitle("First view")
}
}
struct SecondView: View {
var body: some View {
Color(.systemGray6)
.ignoresSafeArea()
.navigationTitle("second view")
}
}
struct CustomTabView: View {
#Binding var selectedTab: Tab
var body: some View {
HStack {
Spacer()
Button {
selectedTab = .map
} label: {
VStack {
Image(systemName: "map")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
Text("Map")
.font(.caption2)
}
.foregroundColor(selectedTab == .map ? .blue : .primary)
}
.frame(width: 60, height: 50)
Spacer()
Button {
} label: {
ZStack {
Circle()
.foregroundColor(.secondary)
.frame(width: 80, height: 80)
.shadow(radius: 2)
Image(systemName: "plus.circle.fill")
.resizable()
.foregroundColor(.primary)
.frame(width: 72, height: 72)
}
.offset(y: -2)
}
Spacer()
Button {
selectedTab = .recorded
} label: {
VStack {
Image(systemName: "chart.bar")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
Text("Recorded")
.font(.caption2)
}
.foregroundColor(selectedTab == .recorded ? .blue : .primary)
}
.frame(width: 60, height: 50)
Spacer()
}
}
}