I am showing a UIViewController via a SwiftUI view, like so:
#available(iOS 13, *)
struct WelcomeNavView: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<WelcomeNavView>) -> UINavigationController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navVc = storyboard.instantiateViewController(withIdentifier: "welcomeNav") as! UINavigationController
return navVc
}
func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<WelcomeNavView>) {
}
}
I then present it from a ViewController like so:
self.viewController?.present(style: .fullScreen) {
WelcomeNavView()
}
However, it does not occupy the entire screen and the UIHostViewController color is showing at the top and bottom:
How can I change the color of the UIHostingViewController's view.. Or expand the View it is holding to occupy the entire screen?
Another simple and quick solution is, you can ignore safe area of your WelcomeNavView() while presenting.
example
iOS 13:
WelcomeNavView().edgesIgnoringSafeArea(.all)
iOS 14 and above:
WelcomeNavView().ignoresSafeArea()
Here is the solution,
I ran into similar problem, wherein I had to set hosting view controller's background clear.
Apparently, rootView (probably our SwiftUI View) of hostingVC and hostingVC.view are different.
so this would allow you to change to background color.
hostingVC.view.backgroundColor = .clear
Of course use any color in place of ".clear".
Just keep in mind to change color of SwiftUI view passed as rootView to hostingVC accordingly, mostly make that clear, so it won't be shown against above set color.
Your_SwiftUI_View.background(Color.clear)
Related
I have a UIViewRepresentable that represents a PKCanvasView.
struct PKCanvasRepresentable : UIViewRepresentable
{
#Binding var canvas: PKCanvasView
func makeUIView(context: Context) -> PKCanvasView {
canvas.tool = PKInkingTool(.pen, color: .black, width: 2)
canvas.drawingPolicy = .anyInput
canvas.isOpaque = false
canvas.backgroundColor = .clear
return canvas
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {}
}
I want to use it as part of a sheet, that contains other input components and must be vertically scrollable.
#State var canvas = PKCanvasView()
var body: some View {
NavigationView {
ScrollView {
VStack {
// ..various components..
PKCanvasRepresentable(canvas: $canvas)
}
}
}
}
The drawing does not work, because the drawing gesture gets canceled by the scroll gesture.
I would like the PKCanvasView related gestures having priority over the scroll view ones. How can i achieve this?
Example
The expected behaviour can be seen when - for example - a DatePicker in wheel style is in a ScrollView. The scroll view does not receive any gesture input when interacting with the DatePicker. I would like to have the same behaviour for a PKCanvasView.
Additional info
I tried to add various Gesture modifiers to the Representable which prevents the ScrollView from getting input events - but of course that also prevents the Canvas from getting user input.
I built a drawing component myself in the past which worked, because i had control over the Gestures that were added to make the component happen. But i would prefer to use PKCanvasView, which does everything i need already - except from the described issue.
I saw this question - but it has nothing to do with PKCanvasView and its solution does not help.
I tried ai based code generators - but i don't have any subscriptions so i'm limited in tries and length of the answer. I tried the following quote, which produced only invalid, tutorial level answers:
Write a SwitUI view that contains of a ScrollView which has a nested UIViewRepresentable of a PKCanvasView where the ScrollView does not receive any kind of user input, events or gestures while the user interacts with the PKCanvasView and tries to draw but functions normally whenever the user does not interact with the PKCanvasView
We have an app which uses a mix of UIKit and SwiftUI. The nav hierarchy is:
UIKit Tab Bar > UIKit Table > SwiftUI View > UIKit View
as of iOS 15 the status bar is not taking on the color of the nav bar:
I'm trying to figure out if this is something I can fix.
Looking in Reveal it looks as tho the UIHostingView has a white bg:
Is it possible write code to change the bg color of the status bar in this setup?
This doesn't seem possible in a UIViewControllerRepresentable? And the hosted view is not taking up the whole screen, so changing that has no impact(?)
import SwiftUI
import PaddleCloud
struct PaddleMapView: UIViewControllerRepresentable {
typealias UIViewControllerType = MapViewController
var trip: Trip
var tripSpeeds: TripSpeeds
func makeUIViewController(context: Context) -> MapViewController {
let sb = UIStoryboard(name: "Map", bundle: nil)
let mapVC = sb.instantiateInitialViewController() as! MapViewController
mapVC.trip = trip
mapVC.tripSpeeds = tripSpeeds
return mapVC
}
}
NB: Changing the bg color in reveal does not change anything in the app.
I have referred to this Stack Overflow thread SwiftUI How to Pop to root view.
However it doesn't work for my case, of course no answer fits each persons use case perfectly so one always has to modify the answer slightly but of course keeping to the overall outline of the solution.
I have looked at this thread How to popup multiple view off navigation stack, but I am not sure resetting the scene is the best option? There has got to be a "normal" way?
The solution I went with is use an ObservableObject and set it as an EnvironmentObject of the root view.
The navigation stack grows by 4 views:
RootView().environmentObject(AppSettings.shared)
FirstView()
SecondView()
ThirdView()
FourthView()
The NavigationLink isActive state for the FirstView is defined in AppSettings.shared, all the other states are found in the subsequent view and not in AppSettings.
For example:
FirstView -> SecondView the SecondView isActive state is defined in the ViewModel of the FirstView, and so on and so forth.
What I am trying to achieve is to pop to RootView from the FourthView. So on the FourthView there is an environmentObject variable of type AppSettings (passed down as an EnvironmentObject from RootView) and on button press, toggle RootView -> FirstView isActive state to false.
It toggles but there's no navigation.
However, in the debug console this is the error
Trying to pop to a missing destination at /Library/Caches/com.apple.xbs/Sources/Monoceros/Monoceros-42.24.100/Shared/NavigationBridge_PhoneTV.swift:205
From my understanding toggling that state to false should trigger a navigation back, but in the Stack Overflow thread there was a post to use #State variable from RootView -> FirstView and in the EnvironmentObject have a variable moveToDashbord. Then on the RootView add a .onReceive modifier to listen to moveToDashboard publisher and then trigger #State variable. But again that also results in the same debug console message.
In short all solutions result in a missing destination console message. Is this because the navigation is too deep?
This is an iPad only project, and the navigationView style is set to StackedNavigationStyle.
System Details:
Xcode 11.6
iOS/ PadOS Target 13.0 (so not using SwiftUI 2.0, if that is a thing)
Code examples:
This is the SceneDelegate which sets the AppSettings as system wide EnvironmentObject.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = LoginView().environmentObject(AppSettings.shared)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
This is an example of the navigationLink to the RootView
NavigationLink(destination: RootView().navigationBarBackButtonHidden(true), isActive: self.$loginViewModel.loginSuccess) {
EmptyView()
}
This is an example of navigationLink from RootView -> FirstView:
NavigationLink(destination: FirstView().environmentObject(FirstViewModel()), isActive: self.$appSettings.firstView) {
EmptyView()
}.isDetailLink(false)
Note. I had to change the actual names of the Views for clarification sake.
I found the issue, on one of the views there was a UIViewControllerRepresentable wrapped UIAlertController. It was structured as a view modifier with provision for content.
This basically wrapped over the original view which ended up breaking the navigation stack.
The solution was to rather wrap a UIViewController which is presented by the View and in that ViewController resides the UIAlertController.
This solution for the UIAlertController was used and adapted SwiftUI - UIAlertController
I am using SwiftUI and bridge to UITextField (I need to assign firstResponder). I use the code from here: SwiftUI: How to make TextField become first responder?
The following code updates my #Binding var. This of course is intended behavior. The problem is that this causes the textField to ignore the frame/ bounds. It just expands and pushes other elements out of the view. Other parts of the view are also reliant on the #State.
func textFieldDidChangeSelection(_ textField: UITextField) {
text = textField.text ?? ""
}
How can I update the #State and still keep the textField in the frame?
You need to decrease priority of content resistance in makeUIView (so content would not push external layout set in SwiftUI) like below
func makeUIView(context: UIViewRepresentableContext<MyField>) -> UITextField {
let field = UITextField(frame: .zero)
field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
...
I currently have several labels and buttons in a UIView. I am using Storyboards and I want to add an image to the background. I tried it this way:
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(patternImage: UIImage(named:"background.png")!)
But unfortunately, this does not look right in the larger screen sizes. I think that I need to add a UIImageView. When I added a UIImageView, I couldn't figure out how to set it to be in the background so my button and labels could still be seen.
let someImageView: UIImageView = {
let theImageView = UIImageView()
theImageView.image = UIImage(named: "yourImage.png")
return theImageView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(someImageView) //This add it the view controller without constraints
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
someImageView.frame = view.bounds
}
In your storyboard:
1- Add a UIImageView (inside your main view and outside everything else) and set its image to the image you want.
2- setup its constraints properly to fill the screen (give it 0 from all sides).
3- From attribute inspector, set Content Mode property to Aspect Fit.
I solved this problem by going to the panel in Storyboards and moving the UIImageView above the container view that held the labels and buttons. The UIImageView remained inside of the View, but moved to the background (under the buttons and labels).