Posts

Post marked as Apple Recommended
20k Views
So currently what I have been doing for determining what device type and orientation I am in for SwiftUI is using sizeClasses, usually something like the following:struct SizeClassView: View { @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? var body: some View { if horizontalSizeClass == .compact && verticalSizeClass == .regular { Text("iPhone Portrait") } else if horizontalSizeClass == .regular && verticalSizeClass == .compact { Text("iPhone Landscape") } else if horizontalSizeClass == .regular && verticalSizeClass == .regular { Text("iPad Portrait/Landscape") } } }What I'd like to know is: Is there any better way to do this in SwiftUI? The main problem I'm having with this method is that I can't differentiate between iPad Portrait and Landscape, but at least I can use it to differentiate between iPhone Portrait and iPhone Landscape and iPad... Any help and insight is greatly appreciated!
Posted
by TroyRO.
Last updated
.
Post marked as solved
11 Replies
18k Views
I have a SwiftUI application in development, and for most screens, I'm fine with them being either landscape or portrait, and making designs for each orientation. However, for some screens, I would like to only allow the portrait orientation, and a few in only landscape. Essentially, something like the following StackOverflow post, but for SwiftUI:https://stackoverflow.com/questions/25606442/how-to-lock-portrait-orientation-for-only-main-view-using-swiftAnything like this for SwiftUI yet?
Posted
by TroyRO.
Last updated
.
Post not yet marked as solved
2 Replies
1.9k Views
import SwiftUI struct ConditionalNavigationParentView: View { @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? var body: some View { NavigationView { // iPhone Portrait if horizontalSizeClass == .compact && verticalSizeClass == .regular { VStack { Text("This is the Parent View.") .foregroundColor(Color.black) .font(.headline) NavigationLink(destination: ConditionalNavigationChildView()) { Text("Navigate to Child View.") .font(.body) } } } // iPhone Landscape if verticalSizeClass == .compact { HStack { Text("This is the Parent View.") .foregroundColor(Color.black) .font(.headline) NavigationLink(destination: ConditionalNavigationChildView()) { Text("Navigate to Child View.") .font(.body) } } } } } } struct ConditionalNavigationChildView: View { var body: some View { Text("This is the Child View.") .font(.headline) } }In the above code, we have a Parent View and a Child View. The parent view holds a conditional title and navigation link. The child view holds a title. When you navigate to the child view, navigation works fine. However, once the condition that holds the navigation link in the parent view is no longer met (by rotating your device from portrait to landscape), the connection keeping the child and parent views navigatable breaks, and forces navigation back to the parent view. This appears to be because SwiftUI as it is now needs to redraw the components from the parent view. With the example above, one solution would be to make only the text condtional, and have the navgation link be held in both a vstack and hstack simultaneously, like this:import SwiftUI struct ConditionalNavigationParentView: View { @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? var body: some View { NavigationView { VStack { // iPhone Portrait if horizontalSizeClass == .compact && verticalSizeClass == .regular { Text("This is the Parent View.") .foregroundColor(Color.black) .font(.headline) } HStack { // iPhone Landscape if verticalSizeClass == .compact { Text("This is the Parent View.") .foregroundColor(Color.black) .font(.headline) } NavigationLink(destination: ConditionalNavigationChildView()) { Text("Navigate to Child View.") .font(.body) } } } } } } struct ConditionalNavigationChildView: View { var body: some View { Text("This is the Child View.") .font(.headline) } }While this does fix the navigation issue in this case, other cases with multiple navigation links seem to be completely impossible. For example, say you replace the title with another navigation link. The second one will still work fine, as it is not conditional, but the first one, and all of its subviews, will get pushed back to the parent view on rotation.In UIKit, this would never be a problem, as conditional navigation doesn't push back to the parent view like that. So is this a SwiftUI bug? Or is it intended behavior? If it is intended behavior, what solution is there for a view with multiple conditional navigation links?
Posted
by TroyRO.
Last updated
.
Post not yet marked as solved
2 Replies
616 Views
So say we have a set of views, which uses an enum and Environment Objects in order to navigate globally, like the following (if copying remember to add .environmentObject(StatusNavigation()) to SceneDelegate):// // ResetNavigation.swift // SwiftUI Playground Project // // Created by O'Donnell, Troy(AWF) on 12/30/19. // Copyright © 2019 O'Donnell, Troy(AWF). All rights reserved. // import SwiftUI enum StatusViews { case firstView case secondView case thirdView case emptyView } final class StatusNavigation: ObservableObject { @Published var statusNavigation: StatusViews = StatusViews.firstView } struct UniversalNavigation: View { @EnvironmentObject var statusNavigation: StatusNavigation var body: some View { if statusNavigation.statusNavigation == .firstView { return AnyView(FirstView()) } else if statusNavigation.statusNavigation == .secondView { return AnyView(SecondView()) } else if statusNavigation.statusNavigation == .thirdView { return AnyView(ThirdView()) } else if statusNavigation.statusNavigation == .emptyView { return AnyView(EmptyView()) } else { return AnyView(FirstView()) } } } struct FirstView: View { @EnvironmentObject var statusNavigation: StatusNavigation var body: some View { NavigationView { VStack(spacing: 0) { Text("First View") .font(.title) .fontWeight(.bold) .foregroundColor(Color.black) Text("Go to Second View") .foregroundColor(Color.blue) .onTapGesture {self.statusNavigation.statusNavigation = .secondView} NavigationLink(destination: ChildView(idxView: "First")) { Text("Navigate to Child View") } } } } } struct SecondView: View { @EnvironmentObject var statusNavigation: StatusNavigation var body: some View { NavigationView { VStack(spacing: 0) { Text("Second View") .font(.title) .fontWeight(.bold) .foregroundColor(Color.black) Text("Go to Third View") .foregroundColor(Color.blue) .onTapGesture {self.statusNavigation.statusNavigation = .thirdView} NavigationLink(destination: ChildView(idxView: "Second")) { Text("Navigate to Child View") } } } } } struct ThirdView: View { @EnvironmentObject var statusNavigation: StatusNavigation var body: some View { NavigationView { VStack(spacing: 0) { Text("Third View") .font(.title) .fontWeight(.bold) .foregroundColor(Color.black) Text("Go to First View") .foregroundColor(Color.blue) .onTapGesture {self.statusNavigation.statusNavigation = .firstView} NavigationLink(destination: ChildView(idxView: "Third")) { Text("Navigate to Child View") } } } } } struct ChildView: View { var idxView: String @EnvironmentObject var statusNavigation: StatusNavigation var body: some View { VStack(spacing: 0) { Text("\(idxView) Child View") .font(.title) .fontWeight(.bold) .foregroundColor(Color.black) Text("Go back to First Parent View") .foregroundColor(Color.blue) .onTapGesture {self.statusNavigation.statusNavigation = .firstView} Text("Go back to Second Parent View") .foregroundColor(Color.blue) .onTapGesture {self.statusNavigation.statusNavigation = .secondView} Text("Go back to Third Parent View") .foregroundColor(Color.blue) .onTapGesture {self.statusNavigation.statusNavigation = .thirdView} } } }As it is now, we can rotate between the three main views just fine, but I also want to be able to navigate back from the children (and more importantly, children of children!) in order to have a more dynamic and flexible navigation system. For example, if we added more children views to the already-existing children views, and set self.statusNavigation.statusNavigation to the first, second or third views, we can move back to programmer-defined views more easily. However, I have had one problem with this. Say we are in the first child view. We can navigate to the second and third parent view using Universal Navigation just fine, since we aren't in those places right now, but since the first child view is the child of the first parent view, setting the environment object to .firstView again doesn't change it, and thus doesn't update it. What I've done to fix that is by changing the ChildView slightly, like this:struct ChildView: View { var idxView: String @EnvironmentObject var statusNavigation: StatusNavigation var body: some View { VStack(spacing: 0) { Text("\(idxView) Child View") .font(.title) .fontWeight(.bold) .foregroundColor(Color.black) Text("Go back to \(idxView) Parent View") .foregroundColor(Color.blue) .onTapGesture {self.statusNavigation.statusNavigation = .emptyView; DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { self.statusNavigation.statusNavigation = self.changeDestination(idx: self.idxView)}} } } func changeDestination(idx: String) -> StatusViews { if idx == "First" { return .firstView } else if idx == "Second" { return .secondView } else if idx == "Third" { return .thirdView } else { return .firstView } } }Now while this works, A. It's kinda hacky, with using the DispatchMainQueue, and B. Delays aren't a great solution as it will eat up the phone's memory really quick. My questions is this: Is there some function we can call in between the first and second variable change, to make SwiftUI recognize the first change before it sees the second one?
Posted
by TroyRO.
Last updated
.
Post not yet marked as solved
1 Replies
2k Views
In SwiftUI, the main method of navigating from one view to another will be with NavigationViews and NavigationLinks. The default layout for content using NavigationViews varies by orientation and device. For example, iPad Portrait has a "drawer" on the left that can be pulled out by swiping. However, in iPad Landscape, as well as iPhone 8 Plus Landscape among other device versions, the drawer stays open. The following code snippet can be used to replicate what I mean:struct ContentView: View { var body: some View { NavigationView { NavigationLink(destination: Text("DetailView")) { Text("DrawerView") } } } }That being said, what options do we have for forcing the behavior for certain orientations and device models? For example, I would like to be able to either force iPad Portrait to have the same behavior as iPad Landscape, or give it the regular behavior we have on an iPhone 11, where the back button and NavigationBar appear on the top.
Posted
by TroyRO.
Last updated
.
Post not yet marked as solved
0 Replies
422 Views
In SwiftUI, the main method of navigating from one view to another will be with NavigationViews and NavigationLinks. The default layout for content using NavigationViews varies by orientation and device. For example, iPad Portrait has a "drawer" on the left that can be pulled out by swiping. However, in iPad Landscape, as well as iPhone 8 Plus Landscape among other device versions, the drawer stays open. The following code snippet can be used to replicate what I mean:struct ContentView: View { var body: some View { NavigationView { NavigationLink(destination: Text("DetailView")) { Text("DrawerView") } } }}That being said, what options do we have for forcing the behavior for certain orientations and device models? For example, I would like to be able to either force iPad Portrait to have the same behavior as iPad Landscape, or give it the regular behavior we have on an iPhone 11, where the back button and NavigationBar appear on the top.
Posted
by TroyRO.
Last updated
.
Post not yet marked as solved
0 Replies
473 Views
I have a SwiftUI application in development, and for most screens, I'm fine with them being either landscape or portrait, and making designs for each orientation. However, for some screens, I would like to only allow the portrait orientation, and a few in only landscape. Essentially, something like the following StackOverflow post, but for SwiftUI:https://stackoverflow.com/questions/25606442/how-to-lock-portrait-orientation-for-only-main-view-using-swiftAnything like this for SwiftUI yet?
Posted
by TroyRO.
Last updated
.
Post not yet marked as solved
43 Replies
19k Views
Here's the gist of the problem: I have a NavigationLink inside the .navigationBarItems(trailing: LinkHere {ImageHere }) property for a view. When I tap the link, it directs to the new view just fine. However, when I tapthe back button, the app throws the following error, along with a signal abort error on the AppDelegate class line:2019-10-22 12:17:55.403091-0700 MyApp[5203:116164] *** Assertion failure in -[UINavigationController popToViewController:transition:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3900.12.2.1/UINavigationController.m:81292019-10-22 12:17:55.412311-0700 MyApp[5203:116164] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Tried to pop to a view controller that doesn't exist.'*** First throw call stack:( 0 CoreFoundation 0x00007fff23c0b02e __exceptionPreprocess + 350 1 libobjc.A.dylib 0x00007fff50b00b20 objc_exception_throw + 48 2 CoreFoundation 0x00007fff23c0ada8 +[NSException raise:format:arguments:] + 88 3 Foundation 0x00007fff25684b61 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191 4 UIKitCore 0x00007fff470ab08d __57-[UINavigationController popToViewController:transition:]_block_invoke + 620 5 UIKitCore 0x00007fff470aad3a -[UINavigationController popToViewController:transition:] + 753 6 SwiftUI 0x00007fff2c0cb7dd $s7SwiftUI21UIKitNavigationBridgeC3pop33_F345616596EA75D1F4200D7666E5E588LL8animatedySb_tF + 413 7 SwiftUI 0x00007fff2c0caaa3 $s7SwiftUI21UIKitNavigationBridgeC20preferencesDidChangeyyAA14PreferenceListVF + 1267 8 SwiftUI 0x00007fff2c05691d $s7SwiftUI14_UIHostingViewC20preferencesDidChangeyyF + 477 9 SwiftUI 0x00007fff2c1555dd $s7SwiftUI9ViewGraphC13updateOutputs2atyAA4TimeV_tF + 221 10 SwiftUI 0x00007fff2c4a8629 $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtFyyXEfU_yyXEfU_ + 1001 11 SwiftUI 0x00007fff2c4a803a $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtFyyXEfU_ + 634 12 SwiftUI 0x00007fff2c49c094 $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtF + 436 13 SwiftUI 0x00007fff2c637e42 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyF + 226 14 SwiftUI 0x00007fff2c637e65 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyFTo + 21 15 UIKitCore 0x00007fff47ca0dc5 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2478 16 QuartzCore 0x00007fff2b0f3db1 -[CALayer layoutSublayers] + 255 17 QuartzCore 0x00007fff2b0f9fa3 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 517 18 QuartzCore 0x00007fff2b1058da _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 80 19 QuartzCore 0x00007fff2b04c838 _ZN2CA7Context18commit_transactionEPNS_11TransactionEd + 324 20 QuartzCore 0x00007fff2b081b41 _ZN2CA11Transaction6commitEv + 643 21 QuartzCore 0x00007fff2b0824aa _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 76 22 CoreFoundation 0x00007fff23b6d617 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23 23 CoreFoundation 0x00007fff23b680ae __CFRunLoopDoObservers + 430 24 CoreFoundation 0x00007fff23b6872a __CFRunLoopRun + 1514 25 CoreFoundation 0x00007fff23b67e16 CFRunLoopRunSpecific + 438 26 GraphicsServices 0x00007fff383d1bb0 GSEventRunModal + 65 27 UIKitCore 0x00007fff477bcef8 UIApplicationMain + 1621 28 MyApp 0x000000010fd07e0b main + 75 29 libdyld.dylib 0x00007fff51986c39 start + 1)libc++abi.dylib: terminating with uncaught exception of type NSException(lldb) The following StackOverflow link has the same problem for me, and the code is, in essence, the same:https://stackoverflow.com/questions/58404725/why-does-my-swiftui-app-crash-when-navigating-backwards-after-placing-a-navigatHas there been any solution for this? Is it really just a bug?
Posted
by TroyRO.
Last updated
.