I'm playing with @EnvironmentObject to see how it works in SwiftUI. I have the main view (ContentView) where it says the user has not logged in yet. By letting the user tap a link, I want to make it such that they can log in by tapping a button.
class LoginMe: ObservableObject {
@Published var loggedIn = false
}
struct ContentView: View {
@StateObject var loginMe = LoginMe()
var body: some View {
if loginMe.loggedIn {
Text("Yes, I'm logged in")
} else {
NavigationView {
VStack {
Text("No, not logged in yet")
.padding(.vertical)
NavigationLink(destination: LoginView()) {
Text("Tap to log in")
}
}
.navigationBarTitle("User")
}
.environmentObject(loginMe)
}
}
}
struct LoginView: View {
@EnvironmentObject var loginMe: LoginMe
var body: some View {
/*
Toggle(isOn: $loginMe.loggedIn) {
Text("Log in")
}.padding(.horizontal)
*/
Button("Login") {
loginMe.loggedIn.toggle()
}
}
}
So far, when the user taps a button in the LoginView view, the screen goes back to ContentView and the navigation simply disappears. How can I change my code so that the status will change back and forth in in the LoginView view by tapping a button and then so that they can return to ContentView the navigation return button? I think the problem is that I need to use @State var in ContentView and @Binding var in LoginView. Things are kind of confusing. Muchos thankos.
Post
Replies
Boosts
Views
Activity
I have the following lines of code for showing a list of friends.
import SwiftUI
struct ContentView: View {
@State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"]
@State var editMode = EditMode.inactive
var body: some View {
NavigationView {
List {
ForEach(users, id: \.self) { user in
Text(user)
}
}
.navigationBarTitle("Friends")
.environment(\.editMode, $editMode)
.navigationBarItems(leading: Button("Edit", action: {
if self.editMode == .active {
self.editMode = .inactive
} else {
self.editMode = .active
}
}))
}
}
}
If you see the code at the bottom, I have four lines just in order to change the value of editMode. Does SwiftUI have something like
showDetails.toggle()
where showDetails is a Boolean variable? Muchos thankos.
I have the following lines of code to work with a list of strings.
import SwiftUI
struct ContentView: View {
@State private var users = ["George", "Kenny", "Susan", "Natalie"]
var body: some View {
NavigationView {
List {
ForEach(users, id: \.self) { user in
Text(user)
}
.onDelete(perform: delete)
}
.navigationBarTitle("My family")
.toolbar {
EditButton()
}
}
}
func delete(at offsets: IndexSet) {
users.remove(atOffsets: offsets)
}
}
Now, I'm doing the following out of curiosity. Now, I have a button in naviationBarItems. And I wonder if I can turn on and off the edit feature of the list with the button?
struct ContentView: View {
@State private var users = ["George", "Kenny", "Susan", "Natalie"]
var body: some View {
NavigationView {
List {
ForEach(users, id: \.self) { user in
Text(user)
}
}
.navigationBarTitle("My family")
.navigationBarItems(trailing:
Button(action: {
print("Edit button pressed...")
}) {
Text("Edit")
}
)
}
}
}
Muchos thankos.
I just want to show a simple navigation title like the following.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
Color.red.edgesIgnoringSafeArea(.all)
Text("Hello")
}
.navigationTitle("GGG")
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(false)
}
}
}
And I get a bunch of mumbo jumbo auto-layout warnings (Unable to simultaneously satisfy constraints...) in Console. If I comment out the navigationTitle line, I won't get them. I have never seen those messages in showing a navigation title when writing code with UIKit. What am I doing wrong? Muchos thankos
I'm trying to understand how Combine works. The following is my sample code.
import UIKit
import Combine
class ViewController: UIViewController {
// MARK: - Variables
var cancellable: AnyCancellable?
// MARK: - IBAction
@IBAction func buttonTapped(_ sender: UIButton) {
currentValueSubject.send(20)
}
// MARK: - Life cycle
var currentValueSubject = CurrentValueSubject<Int, Never>(1)
override func viewDidLoad() {
super.viewDidLoad()
let cancellable = currentValueSubject
.sink { value in
print("New value: \(value)")
}
currentValueSubject.send(5)
currentValueSubject.send(10)
//currentValueSubject.send(completion: .finished)
currentValueSubject.send(15)
//cancellable.cancel()
}
}
If I run it with the iPhone simulator, I get
New value: 1
New value: 5
New value: 10
New value: 15
If I tap the button, the app won't get a new value. I suppose that's because the subscription is cancelled at the end of viewDidLoad? If so, why does it get cancelled? I don't quite see a practical side of Combine's Subject. When is it useful? Thanks.
I have the following lines of code in practicing Combine.
import UIKit
import Combine
class ViewController: UIViewController {
// MARK: - Variables
var cancellable: AnyCancellable?
@Published var segmentNumber: Int = 0
// MARK: - IBOutlet
@IBOutlet weak var actionButton: UIButton!
// MARK: - IBAction
@IBAction func segmentChanged(_ sender: UISegmentedControl) {
segmentNumber = sender.selectedSegmentIndex
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
cancellable = $segmentNumber.receive(on: DispatchQueue.main)
.assign(to: \.isEnabled, on: actionButton)
}
}
I get an error at .assign that says
Value of type 'UIView?' has no member 'isEnabled'
What am I doing wrong? Thank you.
Hello. I'm a little bit confused about how TestFlight works. If I have an iOS app under development that has not been in the store and that has not been submitted for a review yet, can I use TestFlight to have it tested by my development team? I know that there are two types of tests, internal tests and external tests. It seems that you can use TestFlight for internal tests even if the app has not been submitted for a review. Thanks.
When you develop an iOS app for some company, say Company A, under whose name should you sign up a developer account so that you can code-sign the app and send it to the iTunes Connect server? Is it you or Company A? I am thinking that I can sign up a new account to submit an app as long as I give the copyright to Company A. My concern is that there was a new rule like Spamming that took effect several years ago. I guess some guys were using the same package and only changed superficial aspects to submit a ton of similar apps. Thanks.
p.s. It's not an in-house app under an enterprise account that I'm talking about. The app will be submitted to the App Store.
Let me suppose that I have an Xcode project that uses the following Cocoapods.
	pod 'Firebase/Auth'
	pod 'GoogleSignIn'
And let me also suppose that the minimum deployment target for my Xcode project is 11.0. In this case, do I set the Cocoapod platform to 11.0 like
platform :ios, '11.0'
target 'MyProject' do
	use_frameworks!
	pod 'Firebase/Auth'
	pod 'GoogleSignIn'
end
Or do I use the latest versions of Cocoapods like
platform :ios, '14.2'
target 'MyProject' do
	use_frameworks!
	pod 'Firebase/Auth'
	pod 'GoogleSignIn'
end
?
Thanks.
I have a SwiftUI desktop application. And I need to open a window sheet from a storyboard with a click of a button, which works. But I have a problem.
The opening window sheet is very big. Its size is 1,400 x 300 pixels. (I don't know the exact height.) I don't know where this size comes from. But I need to make it smaller. If I try to do it with the view controller, it doesn't work. How can I control the opening window sheet size?
// SwiftUI View //
import SwiftUI
struct ContentView: View {
		@State private var sheetPresented = false
		@State private var selectionIndex = 3
		
		var body: some View {
				ZStack {
						VStack {
								Button(action: {
										sheetPresented = true
								}) {
										Text("Show me a sheet")
								}
								.sheet(isPresented: $sheetPresented) {
										SheetViewControllerRepresentation(message: String(selectionIndex))
								}
						}
				}.frame(minWidth: 360, idealWidth: 360, maxWidth: 360, minHeight: 240, idealHeight: 240, maxHeight: 240, alignment: .center)
		}
}
// View controller //
import Cocoa
import SwiftUI
class SheetViewController: NSViewController {
		// MARK: -
		var message = String()
				
		// MARK: - IBOutlet
		@IBOutlet weak var messageLabel: NSTextField!
		// MARK: - IBAction		
		@IBAction func closeClicked(_ sender: NSButton) {
				/* closing window */
				self.view.window?.setIsVisible(false)
				self.view.window?.close()
		}
		// MARK: - Life cycle
		override func viewDidLoad() {
				super.viewDidLoad()
				// Do view setup here.
		}
		
		override func viewWillAppear() {
				super.viewWillAppear()
				
				messageLabel.stringValue = message
		}
		
		override func viewDidAppear() {
				super.viewDidAppear()
				
				view.setFrameSize(CGSize(width: 320, height: 220))
		}
}
struct SheetViewControllerRepresentation: NSViewControllerRepresentable {
		var message = String()
		
		func makeNSViewController(context: NSViewControllerRepresentableContext<SheetViewControllerRepresentation>) -> SheetViewController {
				let mainStoryboard = NSStoryboard(name: "Main", bundle: nil)
				let sheetViewController = mainStoryboard.instantiateController(withIdentifier: "SheetView") as! SheetViewController
				sheetViewController.message = self.message
				return sheetViewController
		}
		
		func updateNSViewController(_ nsViewController: SheetViewController, context: NSViewControllerRepresentableContext<SheetViewControllerRepresentation>) {
		}
}
Thank you.
I have a SwiftUI view with a button. I want to open a window sheet from a storyboard. Showing a window sheet isn't a problem. It's just that the application no longer has a menu bar. As soon as I created a storyboard and a view controller (NSViewController), the application stopped showing up properly. Now, if I try to debug it (Command + R), the window won't appear. So I have to click on the Live Preview button on the Canvas. If I do, I can click on the Bring Forward button. Finally, if I click on it, the application window always appears at the bottom-left corner of the desktop without the menu bar. So what's the issue?
Anyway, the following is my code.
// SwiftUI View //
import SwiftUI
struct ContentView: View {
		@State private var sheetPresented = false
		@State private var selectionIndex = 3
		
		var body: some View {
				ZStack {
						VStack {
								Button(action: {
										sheetPresented = true
								}) {
										Text("Show me a sheet")
								}
								.sheet(isPresented: $sheetPresented) {
										SheetViewControllerRepresentation(message: String(selectionIndex))
								}
						}
				}.frame(minWidth: 360, idealWidth: 360, maxWidth: 360, minHeight: 240, idealHeight: 240, maxHeight: 240, alignment: .center)
		}
}
// View controller //
import Cocoa
import SwiftUI
class SheetViewController: NSViewController {
		// MARK: -
		var message = String()
		
		
		// MARK: - IBOutlet
		@IBOutlet weak var messageLabel: NSTextField!
		
		
		// MARK: - Life cycle
		override func viewDidLoad() {
				super.viewDidLoad()
				// Do view setup here.
		}
		
		override func viewWillAppear() {
				super.viewWillAppear()
				
				messageLabel.stringValue = message
		}
		
		override func viewDidAppear() {
				super.viewDidAppear()
				
				view.setFrameSize(CGSize(width: 320, height: 220))
		}
}
struct SheetViewControllerRepresentation: NSViewControllerRepresentable {
		var message = String()
		
		func makeNSViewController(context: NSViewControllerRepresentableContext<SheetViewControllerRepresentation>) -> SheetViewController {
				let mainStoryboard = NSStoryboard(name: "Main", bundle: nil)
				let sheetViewController = mainStoryboard.instantiateController(withIdentifier: "SheetView") as! SheetViewController
				sheetViewController.message = self.message
				return sheetViewController
		}
		
		func updateNSViewController(_ nsViewController: SheetViewController, context: NSViewControllerRepresentableContext<SheetViewControllerRepresentation>) {
		}
}
Thank you.
Again, I have a vertical stack of horizontal stacks of buttons as follows.
import SwiftUI
struct ContentView: View {
		@State private var eventPresented = Bool()
		@State private var selectedEventIndex = Int()
		@State private var monthSelection = Int()
		
		var body: some View {
				VStack {
						VStack(spacing: 0.0) {
								HStack(spacing: 0.0) {
										ForEach((0...6), id: \.self) {
												index in
												Button(buttonTitles[index] ?? "") {
														eventPresented = true
														selectedEventIndex = index + 1 - self.weekIndex
												}
												.foregroundColor(titleColors[index])
												.overlay(Text(eventNumbers[index] ?? "").font(.footnote).foregroundColor(.blue).offset(x: -16, y: -16))
												.buttonStyle(BorderlessButtonStyle())
												.frame(width: 48, height: 48, alignment: .center)
												.background(RoundedRectangle(cornerRadius: 2)
																				.fill(fillColors[index])
																				.shadow(color: shadowColors[index], radius: 2, x: 0, y: 0)
												)
												.sheet(isPresented: $eventPresented) {
														EventView(eventVisible: self.$eventPresented, dayFromParent: self.$selectedEventIndex, monthFromParent: self.$monthSelection)
												}
										}
								}
								...
								...
								HStack(alignment: .top, spacing: 0.0) {
										ForEach((35...36), id: \.self) {
												index in
												Button(buttonTitles[index] ?? "") {
														eventPresented = true
														selectedEventIndex = index + 1 - self.weekIndex
												}
												.foregroundColor(titleColors[index])
												.overlay(Text(eventNumbers[index] ?? "").font(.footnote).foregroundColor(.blue).offset(x: -16, y: -16))
												.buttonStyle(BorderlessButtonStyle())
												.frame(width: 48, height: 48, alignment: .center)
												.background(RoundedRectangle(cornerRadius: 2)
																				.fill(fillColors[index])
																				.shadow(color: shadowColors[index], radius: 2, x: 0, y: 0)
												)
												.sheet(isPresented: $eventPresented) {
														EventView(eventVisible: self.$eventPresented, dayFromParent: self.$selectedEventIndex, monthFromParent: self.$monthSelection)
												}
										}
								}
								.frame(width: 336.0, height: 48.0, alignment: .leading)
						}
				}
		}
}
And I want to send a few variables to EventView when the user clicks on a button.
struct EventView: View {
		@Binding var eventVisible: Bool
		@Binding var dayFromParent: Int
		@Binding var monthFromParent: Int
		var body: some View {
				VStack {
						Text("Window sheet.")
						Button("OK") {
								self.eventVisible = false
								print("month from parent: \(monthFromParent)")
								print("day from parent: \(dayFromParent)")
						}
				}
				.frame(width: 240, height: 180)
		}
}
If I just want to send two variables to it,
EventView(eventVisible: self.$eventPresented, dayFromParent: self.$selectedEventIndex)
the compiler didn't complain. For the third variable, it says
SwiftUI the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions I know what it means. Some say it could resolve the issue by making the data types of variables clearer. Others say you could use a function to return a variable for a somewhat complex algebra equation. But what can I do in my case? Does anybody have any suggestions?
Thank you
I have several vertical stacks of six horizontal buttons. And I want to open a modal sheet based on the button they click on.
import SwiftUI
struct ContentView: View {
		@State private var eventPresented = false
		@State private var selectedEventIndex = 3
		
		@State var shadowColors: [Color] = Array(repeating: Color.clear, count: 38)
		@State var titleColors: [Color] = Array(repeating: Color.black, count: 38)
		@State var fillColors: [Color] = Array(repeating: Color.clear, count: 38)
		@State var buttonTitles: [String?] = Array(repeating: nil, count: 38)
		@State var eventNumbers: [String?] = Array(repeating: nil, count: 38)
		
		var body: some View {
				VStack {
						VStack(spacing: 0.0) {
								HStack(spacing: 0.0) {
										ForEach((0...6), id: \.self) {
												Button(buttonTitles[$0] ?? "") {
														eventPresented = true
														selectedEventIndex = 5 // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
												}
												.foregroundColor(titleColors[$0])
												.overlay(Text(eventNumbers[$0] ?? "").font(.footnote).foregroundColor(.blue).offset(x: -16, y: -16))
												.buttonStyle(BorderlessButtonStyle())
												.frame(width: 48, height: 48, alignment: .center)
												.background(RoundedRectangle(cornerRadius: 2)
																				.fill(fillColors[$0])
																				.shadow(color: shadowColors[$0], radius: 2, x: 0, y: 0)
												)
												.sheet(isPresented: $eventPresented) {
														EventView(eventVisible: self.$eventPresented, valueFromParent: self.$selectedEventIndex)
												}
										}
								}
								...
								...
								...
								HStack(alignment: .top, spacing: 0.0) {
										ForEach((35...36), id: \.self) {
												Button(buttonTitles[$0] ?? "") {
														eventPresented = true
														selectedEventIndex = 5
												}
												.foregroundColor(titleColors[$0])
												.overlay(Text(eventNumbers[$0] ?? "").font(.footnote).foregroundColor(.blue).offset(x: -16, y: -16))
												.buttonStyle(BorderlessButtonStyle())
												.frame(width: 48, height: 48, alignment: .center)
												.background(RoundedRectangle(cornerRadius: 2)
																				.fill(fillColors[$0])
																				.shadow(color: shadowColors[$0], radius: 2, x: 0, y: 0)
												)
												.sheet(isPresented: $eventPresented) {
														EventView(eventVisible: self.$eventPresented, valueFromParent: self.$selectedEventIndex)
												}
										}
								}
								.frame(width: 336.0, height: 48.0, alignment: .leading)
						}
				}
		}
}
struct EventView: View {
		@Binding var eventVisible: Bool
		@Binding var valueFromParent : Int
		var body: some View {
				VStack {
						Text("This is a sheet.")
						Button("OK") {
								self.eventVisible = false
								print("From parent: \(valueFromParent)")
						}
				}
				.frame(width: 240, height: 180)
		}
}
For now, I arbitrarily set eventPresented to 5, which will be passed to EventView. How can I set $0 to this state value like
eventPresented = $0
Thank you.
I have two MenuButtons, equivalent to NSPopupButtons in Cocoa. One has a list of years, starting at 2020, and the other lists calendar months. So I have the following test code to see how the MenuButton button works.
import SwiftUI
struct ContentView: View {
		@State var yearSelection = 2020
		@State var monthSelection = 12
		@State var monthLiteral = "December"
		
		var body: some View {
				ZStack {
						VStack {
								Spacer()
										.frame(height: 8)
								HStack {
										Spacer()
												.frame(width: 16)
										MenuButton(String(yearSelection)) {
												ForEach((2020...2030), id: \.self) {
														year in
														Button(String(year)) {
																yearSelection = year
														}
												}
										}.frame(width: 68.0)
										
										MenuButton(monthLiteral) {
												ForEach((1...12), id: \.self) {
														month in
														let monStr = Calendar.current.monthSymbols[month - 1]
														Button(monStr) {
																monthSelection = month
																monthLiteral = monStr
														}
												}
										}.frame(width: 102.0)
										Spacer()
								}
								Spacer()
								Button("Select me") {
										yearSelection = 2024
								}
						}
				}.frame(minWidth: 370, idealWidth: 370, maxWidth: 370, minHeight: 200, idealHeight: 200, maxHeight: 200, alignment: .center)
		}
}
Initially being black, if I use the year menu button to select a year, its title color will turn light gray. If I touch the menu button with the mouse pointer, the title color will turn back black. If I click on the select me push button at the bottom to select a year, again, the title color will turn light gray. Is that how the menu button works? It's kind of a cheap Dutch doll. (No offense to Dutch people...). I think this stack overflow topic - https://stackoverflow.com/questions/65050365/issue-about-menu-title-under-macos-decreased-opacity is what I'm talking about. Or am I doing something wrong? Thank you.
I have the following lines of code where I have several vertical stacks of horizontal stacks of buttons on top of a push button titled 'Click me.'
import SwiftUI
struct ContentView: View {
		@State private var eventPresented = false
		@State var fillColors: [Color] = Array(repeating: Color.white, count: 38)
		@State var buttonTitles: [String?] = Array(repeating: nil, count: 38)
		
		var body: some View {
				VStack {
						/* click me */
						VStack() {
								Button("Click me", action: {
										buttonTitles.removeAll()
										for i in 0..<38 {
												buttonTitles.append(String(i + 1))
										}
										fillColors.removeAll()
										for _ in 0..<2 {
												fillColors.append(Color.clear)
										}
										for _ in 0..<36 {
												fillColors.append(Color.white)
										}
								})
						}
						
						/* cal buttons */
						VStack(spacing: 0.0) {
								HStack(spacing: 0.0) {
										ForEach((0...6), id: \.self) {
												Button(buttonTitles[$0] ?? "") {
														eventPresented = true
												}
												.buttonStyle(BorderlessButtonStyle())
												.frame(width: 48, height: 48, alignment: .center)
												.background(RoundedRectangle(cornerRadius: 2)
																				.fill(fillColors[$0])
																				.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
												)
										}
								}
								...
								...
								...
								HStack(alignment: .top, spacing: 0.0) {
										ForEach((35...36), id: \.self) {
												Button(buttonTitles[$0] ?? "") {
														
												}
												.buttonStyle(BorderlessButtonStyle())
												.frame(width: 48, height: 48, alignment: .center)
												.background(RoundedRectangle(cornerRadius: 2)
																				.fill(fillColors[$0])
																				.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
												)
										}
								}
								.frame(width: 336.0, height: 48.0, alignment: .leading)
						}
				}
		}
}
If I click on 'Click me,' the application will label each button. My question is how I take the function out of this button. If I do something like the following as I used to doing in Cocoa and UIKit, I get an error. (I've created a function named 'makeButtons.')
import SwiftUI
struct ContentView: View {
		@State private var eventPresented = false
		@State var fillColors: [Color] = Array(repeating: Color.white, count: 38)
		@State var buttonTitles: [String?] = Array(repeating: nil, count: 38)
		
		var body: some View {
				makeButtons()
				
				/* cal buttons */
				VStack(spacing: 0.0) {
						HStack(spacing: 0.0) {
								ForEach((0...6), id: \.self) {
										Button(buttonTitles[$0] ?? "") {
												eventPresented = true
										}
										.buttonStyle(BorderlessButtonStyle())
										.frame(width: 48, height: 48, alignment: .center)
										.background(RoundedRectangle(cornerRadius: 2)
																		.fill(fillColors[$0])
																		.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
										)
								}
						}
						...
						...
						...
						HStack(alignment: .top, spacing: 0.0) {
								ForEach((35...36), id: \.self) {
										Button(buttonTitles[$0] ?? "") {
												
										}
										.buttonStyle(BorderlessButtonStyle())
										.frame(width: 48, height: 48, alignment: .center)
										.background(RoundedRectangle(cornerRadius: 2)
																		.fill(fillColors[$0])
																		.shadow(color: Color.black.opacity(0.4), radius: 2, x: 0, y: 0)
										)
								}
						}
						.frame(width: 336.0, height: 48.0, alignment: .leading)
				}.frame(minWidth: 370, idealWidth: 370, maxWidth: 370, minHeight: 420, idealHeight: 420, maxHeight: 420, alignment: .top)
		}
		
		func makeButtons() {
				buttonTitles.removeAll()
				for i in 0..<38 {
						buttonTitles.append(String(i + 1))
				}
				fillColors.removeAll()
				for _ in 0..<2 {
						fillColors.append(Color.clear)
				}
				for _ in 0..<36 {
						fillColors.append(Color.white)
				}
		}
}
Thank you.