import SwiftUI
struct CardTheme: View {
//#State private var theme = 0
#State private var theme = UserDefaults.standard.integer(forKey: "Card Theme")
var body: some View {
List {
HStack {
Text("Mono")
//.font(.system(size: 12))
.onTapGesture {
self.setTheme(i: 0)
}
Spacer()
if(theme == 0) {
Image(systemName: "checkmark")
.foregroundColor(Color.green)
}
}
HStack {
Text("Cool")
// .font(.system(size: 12))
.onTapGesture {
self.setTheme(i: 1)
}
Spacer()
if(theme == 1) {
Image(systemName: "checkmark")
.foregroundColor(Color.green)
}
}
HStack {
Text("Cute")
// .font(.system(size: 12))
.onTapGesture {
self.setTheme(i: 2)
}
Spacer()
if(theme == 2) {
Image(systemName: "checkmark")
.foregroundColor(Color.green)
}
}
}
.navigationBarTitle(Text(verbatim: "Card Theme"))
}
func setTheme(i: Int) {
theme = i
UserDefaults.standard.set(i, forKey: "Card Theme")
}
}
I have a settings menu where the user picks a theme, the default value is set to a global variable, globalVarTheme, which is 0. But after they make a selection, exit that menu, and re-enter the menu it goes back to 0 (the first item) even if they have chosen one of the other items. How do I save their selection?
Also, what is the best way to save user selections beyond the current app session? Should I write all their selections to a plist file or is there a conventional way?
#State is used for the changes within a given view. It is not meant to persist across the views. Instead use #Environment property wrapper. WWDC 2019 talks about that when to use what.
#State isn't the right PropertyWrapper.
If you want to use your settings in multiple views than use #EnvironmentObject as the PropertyWrapper.
You can read about the different PropertyWrappers here:
https://medium.com/#alex.hsieh/state-objectbinding-and-environmentobject-in-swiftui-783588b60671
If you want to save the Settings beyond the current app session you can use UserDefauls.
How do I use UserDefaults with SwiftUI?
Related
I'm planning to add a collection view like you can see in apple musics "browse" or the app store "apps" tab into my SwiftUI App. I tried with scroll view and stacks but it does not have this smooth movement effect. I figured that you could use NSCollectionView Layout and Diffiable Data Source in UIKit for this but I have no idea how to make it in SwiftUI. Any thoughts?
Heres is my code:
struct DiscoverPopularUsersViewPhone: View {
// MARK: Variables and Constants
#State private var fetchedUserAccounts: [UserAccount] = [UserAccount]()
var geometry: GeometryProxy
#State private var userResultsLoadState: LoadState = .inactive
// MARK: Body
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Divider()
.padding(.horizontal, 18)
HStack(spacing: 16) {
Text("Popular Users")
.font(Font.title3.weight(.bold))
.foregroundColor(Color.primary)
Spacer()
NavigationLink(destination: Text("Popular Users")) {
Text("View more")
.font(Font.callout.weight(.regular))
}
}
.padding(.horizontal, 18)
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 12) {
ForEach(fetchedUserAccounts) { account in
DiscoverUserGridItem(userAccount: account, size: (geometry.size.width - 18 * 2 - 12) / 2)
}
}
.padding(.horizontal, 18)
}
}
.onAppear(perform: fetchPopularUsers)
}
}
The fetchPopularUsers method just fetches some user data
I am aware that there is not really a way to do this and it is not an error. But I want it a bit different even if this means I need a whole new approach.
You can see the snappy effect I want to achieve at the top of the App Store app. I could not include a video because it would be too large
Note: I would like the user to see that he is able to scroll so I think the PageTabViewStyle() modifier of TabView is also not an option
I have a pretty usual app with a TabView. However, when a particular process is happening in one of the content views, I would like to prevent the user from switching tabs until that process is complete.
If I use the disabled property on the TabView itself (using a #State binding to drive it), then the entire content view seems disabled - taps don't appear to be getting through to buttons on the main view.
Example:
struct FooView: View {
var body: some View {
TabView {
View1().tabItem(...)
View2().tabItem(...)
}
.disabled(someStateVal)
}
}
Obviously, I want the View1 to still allow the user to, you know, do things. When someStateVal is true, the entire View1 doesn't respond.
Is there a way to prevent changing tabs based on someStateVal?
Thanks!
I could not find a way to individually disable a tabItem, so here is
an example idea until someone comes up with more principled solution.
The trick is to cover the tab bar with a clear rectangle to capture the taps.
struct ContentView: View {
#State var isBusy = false
var body: some View {
ZStack {
TabView {
TestView(isBusy: $isBusy)
.tabItem {Image(systemName: "globe")}
Text("textview 2")
.tabItem {Image(systemName: "info.circle")}
Text("textview 3")
.tabItem {Image(systemName: "gearshape")}
}
VStack {
Spacer()
if isBusy {
Rectangle()
.fill(Color.white.opacity(0.001))
.frame(width: .infinity, height: 50)
}
}
}
}
}
struct TestView: View {
#Binding var isBusy: Bool
var body: some View {
VStack {
Text("TestView")
Button(action: {
isBusy.toggle()
}) {
Text("Busy \(String(isBusy))").frame(width: 170, height: 70)
}
}
}
}
I use another trick. Just hide the tab image.
struct FooView: View {
var body: some View {
TabView {
View1().tabItem{Image(systemName: someStateVal ? "": "globe")}
View2().tabItem{Image(systemName: someStateVal ? "": "gearshape")}
}
}
}
I tried to do a app that pop out a temporary alert that only appear for 1 or 2 seconds. It’s something like App Store rating.
But I don’t know what this called in swiftui. Can anyone answer me?
That is just a view that is shown or hidden conditionally. Here is a complete example that uses a ZStack to place the thank you view over the other view content. The thank you view is either present or not based upon the #State variable showThankYou. DispatchQueue.main.asyncAfter is used to remove the view after 3 seconds.
struct ContentView: View {
#State private var showThankYou = false
var body: some View {
ZStack {
VStack {
Spacer()
Text("Stuff in the view")
Spacer()
Button("submit") {
showThankYou = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.showThankYou = false
}
}
Spacer()
Text("More stuff in the View")
Spacer()
}
if showThankYou {
RoundedRectangle(cornerRadius: 16)
.foregroundColor(Color.gray)
.frame(width: 250, height: 250)
.overlay(
VStack {
Text("Submitted").font(.largeTitle)
Text("Thanks for your feedback").font(.body)
}
)
}
}
}
}
I have the scenario where I intend on using a menu ('MenuView') that varies if the device is in portrait / landscape mode. I am using size classes to determine the device type and this successfully redraws the view on rotation. The menu uses navigation view/links to take you to a further view (the 'DetailedView'); this view also has differing views for portrait and landscape. Again I'm using size classes to successfully redraw the view based on the rotation.
However, what I find is that when I'm in the DetailedView and rotate the device, the display jumps straight back to the MenuView as, of course, it has recognized the size class change and adjusted that view. I would like the display to remain in this view.
How can I prevent the app from jumping to the 'MenuView' when I rotate the device that is displaying the 'DetailedView'? Code from the MenuView below:
Note: I'm using the StackNavigationViewStyle in this instance.
Any help would be gratefully received, thanks in advance!
struct MenuView: View {
#Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
#Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
var body: some View {
NavigationView {
Color("MainBg")
.edgesIgnoringSafeArea(.all)
if horizontalSizeClass == .compact {
PortraitMenuView()
} else {
LandscapeMenuView()
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
Update: DetailedView - This is called from the PortraitMenuView and LandscapeMenuView. Included code from PortraitMenuView - the same navigation link is included in the LandscapeMenuView
struct DetailedView: View {
#Environment(\.horizontalSizeClass) var sizeHClass
#Environment(\.verticalSizeClass) var sizeVClass
var formulae: Functions
var body: some View {
ZStack {
Color.offWhite
.edgesIgnoringSafeArea(.all)
VStack {
Group {
if sizeHClass == .compact && sizeVClass == .compact {
DetailedViewLandscape()
} else {
DetailedViewPortrait()
}
}
}
}
}
}
struct PortraitMenuView: View {
var body: some View {
VStack {
ZStack {
Circle()
.fill(Color("kMainBg"))
.frame(width: 300, height: 300)
.overlay(
Text("FORMULA FINDER")
.font(.largeTitle).bold()
.multilineTextAlignment(.center)
.minimumScaleFactor(0.005)
.lineLimit(2)
.frame(width: 210, height: 210)
)
}
Spacer()
NavigationLink(destination: DetailedView()) {
TileView(title: "Formulae", subtitle: "Functions and formulas", boxColor: Color.pastelGreen)
}.offset(x: 40)
}.padding(.bottom, 20)
}
}
I am running Xcode 11 GM2 and having issues with a NavigationLink not triggering where it is expected. The app I am working on is based on Apple's sample SwiftUI app found here. The problem I am having also happens in Apple's sample.
When I add the NavigationLink to the photos:
NavigationLink(destination: PhotoDetail(photo: photo)) {
PhotoItem(photo: photo)
.frame(width: 300)
.padding(.trailing, 30)
}
The link is not accessible when you click on the image and the other content within the PhotoItem. But if you move up a bit and touch/click just below the Category name above the image, it not only triggers the link to the destination, it triggers it for all five of the items inside the scroll view.
Here is the full code from the Home page and the PhotoRow:
Home
struct HomeView: View {
// Create a categories dictionary
var categories: [String:[Photo]] {
.init(
grouping: photoData,
by: { $0.category.rawValue }
)
}
var body: some View {
NavigationView {
List(categories.keys.sorted(), id: \String.self) { key in
PhotoRow(categoryName: "\(key)".uppercased(),
photos: self.categories[key]!)
.frame(height: 320)
.padding(.top)
}
.navigationBarTitle(Text("Portfolio"))
}
}
}
PhotoRow
struct PhotoRow: View {
var categoryName: String
var photos: [Photo]
var body: some View {
VStack(alignment: .leading){
Text(self.categoryName)
.font(.title)
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top) {
ForEach (self.photos) { photo in
// add navigation to detail view:
NavigationLink(destination: PhotoDetail(photo: photo)) {
PhotoItem(photo: photo)
.frame(width: 300)
.padding(.trailing, 30)
}
}
}
}
}
}
}
Any help resolving this would be appreciated.