SwiftUI forcing individual views to different orientation for iPad - ios

I am listening for existing orientation changes by using UIDevice.orientationDidChangeNotification, but is there a way to lock a SwiftUI View into a particular orientation?
I do not want to introduce AppDelegate.
Example:
import SwiftUI
struct TestView: View {
var text: String
var body: some View {
Text(text)
}
}

Related

Using SwiftUI view in WatchKit and Storyboard

I'm building a watch app with WatchKit and storyboard.
I have a SwiftUI view that I want to use as a subview beside the other Storyboard items in a WKInterfaceController.
The SwiftUI view:
struct SliderView: View {
#State private var speed = 50.0
var body: some View {
CompactSlider(value: $speed, in: 0...100, step: 5) {
Text("Speed")
Spacer()
Text("\(Int(speed))")
}
}
}
This SwiftUI view lays just fine on a separate controller using WKHostingController, but this is not what i want.
class HostingController: WKHostingController<SliderView> {
override var body: SliderView {
return SliderView()
}
}
How can I use this SwiftUI view on Storyboard or WKInterfaceController?

Onboarding screen at launch of SwiftUI document-based app

Is there anyway at all, to show an onboarding screen at the launch of a SwiftUI document-based app, before the file picker screen appears?
What I can do now, is to show the onboarding screen AFTER user creates a new file, or opens an existing file, using UserDefaults:
import SwiftUI
struct ContentView: View {
#Binding var document: AnyDocument
#State var onboarding: Bool
var body: some View {
if onboarding {
OnBoarding(showOnboarding: $onboarding)
} else {
// Normal document view
...
}
}
}

Color change from light-mode to dark-mode not working in SwiftUI on real device

Using Swift5.3.2, iOS14.4.1, Xcode12.4,
I try to make a custom-Color change when switching from light to dark mode in SwiftUI.
Here is my code:
import SwiftUI
struct MyTestView : View {
var body: some View{
VStack {
Rectangle()
.foregroundColor(Color("loginBG"))
}
}
}
The MyTestView is called right at the beginning of my App lifecycle (real simple example):
import SwiftUI
#main
struct myApp: App {
var body: some Scene {
WindowGroup {
MyTestView()
}
}
}
Here is my Color definition of loginBG:
Here is the video showing that the Rectangle does unfortunately not change color - why ?????
(from the status-Bar color-switch you can see that I indeed changed from light-mode to dark-mode and back. But no Rectangle-Color change at all)
You can try with this code:
import SwiftUI
struct MyTestView : View {
var body: some View{
VStack {
Rectangle()
.foregroundColor(Color(UIColor(named: "loginBG")))
}
}
}
replace the Color("loginBG") to Color(UIColor(named: "loginBG"))
This can work for me with real device.

SwiftUI pass by reference a class object to another view

I'm trying to learn SwiftUI and I'm going to develop a simple app with tab views and sharing core motion data between those views.
The main idea is to create a motion manager object (like here) and use the sensor values in all views.
ContentView.swift:
import SwiftUI
struct ContentView: View {
#State private var selection = 1
#State private var viewNames : [String] = ["View1", "View2"]
var body: some View {
TabView(selection: $selection){
View1(viewName: $viewNames[0]).tag(0)
View2(viewName: $viewNames[1]).tag(1)
}
}
}
View1.swift:
import SwiftUI
struct View1 : View {
#Binding var viewName : String
var body: some View {
Text("First View")
.font(.title)
.foregroundColor(Color.gray)
.tabItem {
VStack {
Image(systemName: "star")
Text(viewName)
}
}
}
}
View2.swift:
struct View2 : View {
#Binding var viewName : String
var body: some View {
VStack {
Text("Second View")
.font(.title)
.foregroundColor(Color.green)
.padding(.top)
View21(motionManager: MotionManager())
}.tabItem {
VStack {
Image(systemName:"heart")
Text(viewName)
}
}
}
}
View21.swift
struct View21 : View {
#ObservedObject var motionManager : MotionManager
#State private var showDetails = false
var body: some View{
Text(String(format: "%.2f", motionManager.x))
}
With these code I can use the sensor data in View21, but I can't access the data in the views in the hierarchy above.
Furthermore I created #ObservedObject in the ContentView (like here) and passed it through all views. My app work in the simulator, but it doesn't on the real device.
I can see sensor data changing, but I can't switch the tab views.
I've tried to use #EnvironementObject instead of #ObservedObject, but the behavior is the same.
I'll be very thankful for any help and tipps to my issue.
Best wishes
Okay, so Paulw11 is right that the you probably want to inject your ObservableObject into your environment, then in each view that wants to access that instance, you just add a property with the #EnvironmentObject property wrapper. Below I've thrown together the simplest example I could think of, so that you can get the idea of how it works.
import SwiftUI
import Combine
class ManagerPlaceholder: ObservableObject {
#Published var propertyOne: Double = 1.0
#Published var propertyTwo: Double = 2.0
func action() {
propertyOne = Double.random(in: 0.0..<100.00)
propertyTwo = Double.random(in: 0.0..<100.00)
}
}
struct ContentView: View {
#EnvironmentObject var manager: ManagerPlaceholder
var body: some View {
TabView {
Subview()
.tabItem { Label("First", systemImage: "hexagon.fill") }
.tag(1)
Subview()
.tabItem { Label("Second", systemImage: "circle.fill") }
.tag(2)
}
}
}
struct Subview: View {
#EnvironmentObject var manager: ManagerPlaceholder
var body: some View {
VStack {
Text("Prop One: \(manager.propertyOne)").padding()
Text("Prop Two: \(manager.propertyTwo)").padding()
Button("Change", action: manager.action).padding()
}
}
}
So above is
A simple ObservableObject - All it does is set two Doubles with a random value (notice the properties you want to observe are marked as #Published)
A tab view
A simple sub-view
Notice that both of the views have a #EnvironmentObject var manager: ManagerPlaceholder. You don't set that property directly. That line says that you want to reference a ManagerPlaceholder instance that's in the Environment. The environment is a sort of "pool" of storage that SwiftUI manages for you. You can add an instance of an object to it, then reference it in sub-views that need it.
So to make sure that's in the environment you add it when instantiating a view (could be the tab view, or any super-view). So for example in your _NAME_App.swift (for an iOS 14 target) or SceneDelegate.swift (for an iOS 13 target), you'd instantiate your view like this:
ContentView().environmentObject(ManagerPlaceholder())
If you run the code above you'll see when you hit the Change button it randomly sets the two properties, and both subviews will see the exact same values when you switch back and forth, because they're both referencing the same instance.
Feel free to comment if anything is unclear.

SwiftUI Navigation and status bar clash in color/transparency when inside TabView

My app consists of a few views across a few different tabs inside a TabView. These views create their own NavigationViews. Unfortunately the existence of the TabView is causing their colors and transparency to clash with the app's status bar, which is no longer in line with the navigation bar.
This is easily reproduced in a sample app using the following code.
struct ContentView: View {
var body: some View {
TabView {
NavView()
}
}
}
struct NavView: View {
var body: some View {
NavigationView {
List {
ForEach(0..<10, id: \.self) { _ in
Section(header: Text("Foo")) {
Text("Bar")
}
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("Foobar")
}
}
}
I'm using the grouped list style to make the styling changes more apparent, but it's the same with the default style.
Is there a SwiftUI API to access the status bar styling? Or possibly some other workaround?
As per Apple's documentation, edgesIgnoringSafeArea(_:) should be applied to TabView:
https://developer.apple.com/documentation/swiftui/vsplitview/3288813-edgesignoringsafearea
Extends the view out of the safe area on the specified edges.
struct ContentView: View {
var body: some View {
TabView {
NavView()
} // .edgesIgnoringSafeArea(.top) no longer necessary as of iOS 13.4
}
}
NOTE: It appears Apple changed the default behavior and this is no longer necessary with iOS 13.4.
For some reason, it stopped working properly for me. The situation was it was working without edgesIgnoringSafeArea(_:) applied just for the simulator but not a device. And vice versa when it is applied to it.
Created a little modifier to fix it.
struct EdgeIgnoringSafeAreaModifier: ViewModifier {
var edges: Edge.Set
func body(content: Content) -> some View {
#if targetEnvironment(simulator)
return content
#else
return content.edgesIgnoringSafeArea(self.edges)
#endif
}
}
extension View {
func edgeIgnoringSafeAreaForDevice(_ edges: Edge.Set) -> some View {
self.modifier(EdgeIgnoringSafeAreaModifier(edges: edges))
}
}

Resources