SwiftUI: UIViewRepresentable widget preview - ios

To get a preview of a UIViewRepresentable struct, I have the following code:
import SwiftUI
import WidgetKit
struct ViewRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
return UIView()
}
func updateUIView(_ uiView: UIView, context: Context) {
}
typealias UIViewType = UIView
}
struct ViewRepresentable_Previews: PreviewProvider {
static var previews: some View {
ViewRepresentable().previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
It outputs the following diagnostics.
RemoteHumanReadableError: Failed to update preview
invalid TimelineEntries
The error is "Cannot preview in this file – Failed to update preview"
How do you solve this error?

UIKit views wrapped in UIViewRepresentable will not work in WidgetKit.
Source: https://developer.apple.com/forums/thread/653471?answerId=619627022#619627022

Related

Update Binding in UIViewRepresentable

I have a swiftUI view that calls a UIViewRepresentable view. In the SwiftUI view I am toggling the state of #State boolean value.
In my UIViewRepresentable view I have created a binding that gets past from the main SwiftUI view. The problem is the binding never gets update or at least the updateView function is not getting called in the UIViewRepresentable view. I fell like I must be doing something wrong but I am just overlooking it. Here is an example of what I am trying to do.
import Foundation
import SwiftUI
struct BindingTest: UIViewRepresentable {
#Binding var status: Bool
func makeUIView(context: Context) -> UIActivityIndicatorView {
let activityIndicator = UIActivityIndicatorView()
activityIndicator.style = .large
return activityIndicator
}
func updateUIView(_ uiView: UIViewType, context: Context) {
print("Hello")
}
}
import SwiftUI
struct ChartView: View {
#State var status = false
var body: some View {
VStack{
Spacer()
Button(action: {
status = !status
}) {
Text("Change")
}
BindingTest(status: $status)
}
}
}
I am using Xcode 12.5 and Swift 5.4
Looks like SwiftUI is doing some cleverness under the hood that isn't immediately obvious to us. Because you don't actually use your binding in updateUIView, it's not actually getting called.
However, if you update your code to the following:
func updateUIView(_ uiView: UIViewType, context: Context) {
print("Hello \(status)")
}
then you'll see that it does, in fact, get called.
PS - you can use status.toggle() instead of status = !status

SIGABRT when navigating from ARView with cameraMode = `nonAR` to a regular AR mode ARView

I'm using SwiftUI and RealityKit to make an AR app. I am trying to transition from a nonAR camera mode ARView to a regular ARView using a NavigationLink, but I'm running into a SIGABRT and see the following error whenever I select the link:
validateTextureDimensions, line 1227: error 'MTLTextureDescriptor has width (4294967295) greater than the maximum allowed size of 16384.'
I've reproduced this behavior in a fresh RealityKit app with these simple views:
// ContentView (nonAR ARView)
import SwiftUI
import RealityKit
struct ContentView : View {
var body: some View {
NavigationView {
VStack {
ARViewContainer().edgesIgnoringSafeArea(.all)
NavigationLink(destination: ContentView2()) {
Text("Go!")
}
}
}
}
}
struct ARViewContainer: UIViewRepresentable {
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero, cameraMode: .nonAR, automaticallyConfigureSession: true)
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {}
}
// ContentView2 (AR ARView)
import SwiftUI
import RealityKit
struct ContentView2: View {
var body: some View {
return ARViewContainer2().edgesIgnoringSafeArea(.all)
}
}
struct ARViewContainer2: UIViewRepresentable {
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero)
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {}
}
It seems like some cleanup needs to be performed with the first nonAR ARView before navigating but I'm not sure what the best way to manage that is. I saw the answer in this post and I tried adding a #Binding to ARViewContainer that I set in the parent view to flag to that the uiview should be removed in updateUIView before navigating, but I'm still hitting the crash :/ Any help here would be greatly appreciated!
I ended up resolving this for the time being by using a SceneKit view in place of the nonAR AR view.

How to fix terminal output of "Style Z is requested for an invisible rect" when moving view on default iOS Map, using MapKit and SwiftUI, Xcode 12

Using MapKit and SwiftUI (Version 12.0 beta 2 (12A6163b)) when zooming in/out around the map the terminal produces hundreds of these lines:
2020-07-21 21:05:39.310719-0500 MyApp[95733:4195994] [VKDefault] Style
Z is requested for an invisible rect
import SwiftUI
import MapKit
#main
struct MapTest: App {
var body: some Scene {
WindowGroup {
MapView()
}
}
}
struct MapView: View {
var body: some View {
Map()
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
}
}
struct Map: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.delegate = context.coordinator
return map
}
func updateUIView(_ uiView: MKMapView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, MKMapViewDelegate {
var control: Map
init(_ control: Map) {
self.control = control
}
}
}
How do I fix this?
This is a really common problem since Xcode 12.0, but for now the only solution seems to disable an Environment Variables called "OS_ACTIVITY_MODE".
You have to edit your Scheme, go to Run section, add another Environment Variables, call it "OS_ACTIVITY_MODE" and set "disable" as value.
This prevents messages on log console.

SwiftUI - EnvironmentObject - Strange Memory Usage

I've got some strange results with the memory usage in my SwiftUI App. Below you see a minimal example I've build.
/// Test file for strange behaviour with EnvironmentObject
/// The RAM Usage is increased if we click the button in View "Settings" and switch back to "MapView"
import SwiftUI
import MapKit
class Model: ObservableObject {
#Published var coolBool: Bool = false {
didSet {
print(coolBool.description)
}
}
}
struct MapView: UIViewRepresentable {
private let mapView = MKMapView(frame: .zero)
func makeUIView(context: Context) -> MKMapView {
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
struct Settings: View {
#EnvironmentObject var model: Model
var body: some View {
Form {
Button(action: {self.model.coolBool.toggle()}) {
Text(self.model.coolBool.description)
}
}
}
}
struct ContentView: View {
#EnvironmentObject var model: Model
var body: some View {
TabView() {
MapView().tabItem {
Image(systemName: "map")
Text("1")
}
Settings().tabItem {
Image(systemName: "gear")
Text("2")
}
Text("Just a screen").tabItem {
Image(systemName: "sunrise")
Text("3")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Scenario:
Open App
You will see MapView, the memory usage is stable
Go to Tab 2
Click the button which will change the EnvironmentObject, memory usage still stable
Go to Tab 3, memory usage is the same
Go to Tab 1 (MapView), memory usage is increased
If you redo 3, 4, 6 you will see that the memory usage is going bigger and bigger.
Can someone explain this to me?
The memory usage will start at ~40 MB, if I tap the button -> go to MapView -> tap the button -> go to MapView -> .... the memory usage will increase. I got a memory usage of ~500 MB
Try the following
struct MapView: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero) // created here is managed by SwiftUI
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
Alternate: if you definitely want the one map view - use static
struct MapView: UIViewRepresentable {
private let static mapView = MKMapView(frame: .zero)
func makeUIView(context: Context) -> MKMapView {
MapView.mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}

'FBLoginButton' conform to 'View'

I try to create a Facebook button but when I call it in the VStack it says: 'Referencing initializer 'init(alignment:spacing:content:)' on 'VStack' requires that 'FBLoginButton' conform to 'View'', So why isn't it working because FBLoginButton is a button after all.
here's the code:
struct ContentView: View {
var body: some View {
VStack(alignment: .center) {
FBLoginButton(frame: CGRectMake(0, 0, 100, 100), permissions: ["email"])
}
}
}
First of all needs to create class which implements UIViewRepresentable protocol.
import FBSDKLoginKit
import SwiftUI
struct FacebookLoginButton: UIViewRepresentable {
typealias UIViewType = FBLoginButton
func makeUIView(context: Context) -> UIViewType {
FBLoginButton()
}
func updateUIView(_ uiView: FBLoginButton, context: Context) { }
}
After all this FacebookLoginButton can be used in SwiftUI like this:
var body: some View {
FacebookLoginButton()
}
Facebook SDK still uses UIKit, and FBLoginButton is UIView from UIKit, not View" from SwiftUI.
You need to use loginmanger which does most of the work and embed it inside swiftui Button.
Check link below which will be helpful.
https://gist.github.com/zydeico/8bb9c772e944d1490335e1615aaf4960

Resources