Using SwiftUI view in WatchKit and Storyboard - ios

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?

Related

Update ProgressView value from another ViewController without using #Binding?

Currently I declare my ProgressView in my main view controller which gets it's value from a #State variable. On a button press i change the value of the variable which then updates the value of my progress bar.
I want to make changes to this ProgressView, but using a button on a separate view controller. I've tried to use Binding, but due to the way I am using WatchTabView, this forces me to declare a #Binding variable within my App struct, which isn't possible.
Not sure if I'm overthinking this, but how can i update my progress bar from another view?
Main View
struct ViewController: View {
#State var progressBarValue = 5.0
var body: some View {
ScrollView{
ProgressView("Effort", value: progressBarValue, total: 20)
VStack {
Button{
progressBarValue += 5.0
}label:{
Text("Click")
}
Other View
struct OtherViewController: View{
...
Button{
//I want to increase progressBarValue by clicking here
}label:{
Text("Click")
}
...
}
First please read this post:
What's the difference between a View and a ViewController?
Then read this: https://developer.apple.com/documentation/combine/observableobject
Also, read this: https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject
Finally, come back to this example:
class MyViewController: ObservableObject {
#Published private(set) progress: Float = 0
func updateProgress(by value: Float) {
progress += value
}
}
Parent View:
struct ParentView: View {
#ObservedObject var myController = MyViewController()
var body: some View {
MainChildView()
.environmentObject(myController) //this is one of the ways of passing down an ObservableObject to all underlying views.
//your progress view code can go here
}
}
MainChildView
struct MainChildView: View {
//no need to pass anything into this view because of the EnvironmentObject.
var body: some View {
ChildView()
}
}
ChildView
struct ChildView: View {
#EnvironmentObject var myController: MyViewController //EnvironmentObject will search for MyViewController declared somewhere in one of the parent views
var body: some View {
//your progress view code can go here
Button("Tap", action: {
myController.updateProgress(by: 5)
})
}
}

SwiftUI navigationTitle appear on a wired position and has a layout warning

This is a subView of my project
and it appears at a very low position
xcode also give me the layout warning
import SwiftUI
struct DetailedView: View {
var referFood:ReferFood
#StateObject var recentFood=RecentSearched()
var body: some View {
NavigationView{
Form{
Section("名称")
{
Text("\(referFood.name)")
}
Section("保质期")
{
Text("\(referFood.qualityTime)")
}
Section("储藏方法")
{
Text("\(referFood.method)")
}
}.navigationTitle("详细信息")
.navigationBarTitleDisplayMode(.inline)
}
}
}
It build successfully however when I drag the slider
NavigationView should be in RootView only.
Example in documentation
https://developer.apple.com/documentation/swiftui/navigationlink
is shown below:
Destination View:
struct ColorDetail: View {
var color: Color
var body: some View {
color.navigationTitle(color.description)
}
}
RootView:
NavigationStack {
List {
NavigationLink("Mint") { ColorDetail(color: .mint) }
NavigationLink("Pink") { ColorDetail(color: .pink) }
NavigationLink("Teal") { ColorDetail(color: .teal) }
}
.navigationTitle("Colors")
}
ps:
NavigationStack is the new version of NavigationView which is now deprecated in iOS 16.

Present modal view on navigation to another view in swiftui

Let me know if this is a duplicate question.
I am developing an app with a photo picker. I am on view A and there is a + button on it. When I tap on the +, I want to navigate to a view B. Upon navigation I want to present a Photo Picker view automatically inside view B. I am not able to figure out how to do that presentation of the sheet in Swiftui.
In UIKit,on the viewdidappear of viewcontroller B, i would present the Pickerview.
Here's the code that I have
```import SwiftUI
struct ViewB: View {
#State private var showingImagePicker = false
#State private var uploadedPhotos = [UIImage]()
var body: some View {
VStack {
Button("Select Image") {
showingImagePicker = true
}
}
.sheet(isPresented: $showingImagePicker) {
PhotoPicker(photoList: $uploadedPhotos)
}
.onChange(of: uploadedPhotos) { _ in
}
}
}```
This code is ViewB and I will present the pickerView on tapping a button in viewB. but I don't want to tap on button but instead want the picker to show up on appear of ViewB
There is a view modifier in SwiftUI known as .onAppear(perform:), which is the SwiftUI counterpart to UIKit's viewDidAppear and gives you an entry point upon the construction and display of a SwiftUI view. Simply add this modifier to View B (the view inside the sheet that you are presenting). In the closure that you provide to the modifier, you can change the state to present the picker as needed.
If you'd like the picker view to animate in after the view appears, the appropriate place to declare the transition and animation context is on the view acting as your picker view.
struct ViewB: View {
#State private var displayPicker = false
var body: some View {
VStack {
Text("This is View B")
if displayPicker {
PickerView()
.transition(.slide)
.animation(.easeInOut, value: displayPicker)
}
}
.onAppear {
displayPicker = true
}
}
}
Read more about the modifier here:
https://developer.apple.com/documentation/SwiftUI/AnyView/onAppear(perform:)
Think I figured it out -
struct CreateView: View {
#State private var image: Image?
#State private var showingImagePicker = false
#State private var uploadedPhotos = [UIImage]()
var body: some View {
ScrollView {
HStack {
image?.resizable().scaledToFit()
}
.onAppear {
showingImagePicker = true
}
.sheet(isPresented: $showingImagePicker) {
PhotoPicker(photoList: $uploadedPhotos)
}
.onChange(of: uploadedPhotos) { _ in
guard let uiImage = uploadedPhotos.first else {return}
image = Image(uiImage: uiImage)
}
}
}
}
Here PhotoPicker() is the customview that I have

SwifUI, iOS 13 show view on top of Navigationview in destination view

I am working on app supporting iOS 13 using SwiftUI, I have custom activity indicator(UIViewRepresentable) on which is displayed on Api call
In main screen where I have created NavigationView, on top of that I am able to add the activity indicator view which is able to cover the navigation bar also,
But in detail view which is navigated using navigation link, I tried the same code but the activity indicator view is displayed below navigation bar(which is not covering the navigation bar)
struct DetailView: View {
#State private var show = false
var body: some View {
ZStack {
Button("showSpinner") {
self.show.toggle()
}
}
.modifier(CoverNavigationBar(show: $show) {
CustomActiviyIndicatorView()
.edgesIgnoringSafeArea(.all)
})
} }
struct CoverNavigationBar<Cover: View>: ViewModifier {
#Binding var show: Bool
let cover: () -> Cover
func body(content: Content) -> some View {
ZStack {
content
if self.show {
cover()
}
}
} }

How it is possible to dismiss a view from a subtracted subview in SwiftUI

Whenever my code gets too big, SwiftUI starts acting weird and generates an error:
"The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions"
So I started breaking up my code into Extracted Subviews, one of the problems I came across is how to dismiss a view from a subtracted subview.
Example: we have here LoginContentView this view contains a button when the button is clicked it will show the next view UsersOnlineView.
struct LoginContentView: View {
#State var showUsersOnlineView = false
var body: some View {
Button(action: {
self.showUsersOnlineView = true
}) {
Text("Show the next view")
}
.fullScreenCover(isPresented: $showUsersOnlineView, content: {
UsersOnlineView()
})
}
On the other hand, we have a button that is extracted to subview, to dismiss the modal and go back to the original view:
import SwiftUI
struct UsersOnlineView: View {
var body: some View {
ZStack {
VStack {
CloseViewButton()
}
}
}
}
struct CloseViewButton: View {
var body: some View {
Button(action: {
// Close the Modal
}) {
Text("Close the view")
}
}
}
Give the sbview the state property that defines if the view is shown.
struct CloseViewButton: View {
#Binding var showView: Bool
var body: some View {
Button(
ShowView = false
}) {
Text("Close the view")
}
}
}
When you use the sub view give it the property
CloseButtonView(showView: $showOnlineView)
To allow the sub view to change the isShown property it needs to get a binding.
On the presentation mode. I think this only works with Swiftui presentations like sheet and alert.
The simplest solution for this scenario is to use presentationMode environment variable:
struct CloseViewButton: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
Text("Close the view")
}
}
}
Tested with Xcode 12.1 / iOS 14.1

Resources