Swift: Different title placement depending on configuration - swift3

I have two configurations that are giving me different results:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let rootVC = MainController()
window.rootViewController = rootVC
self.window = window
window.makeKeyAndVisible()
}
class MainController: UIViewController {
var titleLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
titleLabel.text = "Grocery List"
titleLabel.textColor = .black
titleLabel.font = .systemFont(ofSize: 20, weight: .bold)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(titleLabel)
[enter image description here][1]
setupConstraints()
}
func setupConstraints() {
let padding: CGFloat = 15
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: padding),
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
}
First config
However, if I do
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
window?.rootViewController = UINavigationController(rootViewController: MainController())
}
It's different. Why? I think the main problem is I don't really know what the configuration details are doing, so I'm confused on why the different set ups give me different results. Thank you!
Second config

Related

Can't Turn UIKit Into Swift UI With UIViewRepresentable

I have followed two tutorials on UIViewRepresentable and thought the following would work, yet it didn't and I think my situation is more complex than in the tutorials.
Hello, I am trying to turn this code
import SpriteKit
import AVFoundation
class ViewController: NSViewController {
#IBOutlet var skView: SKView!
var videoPlayer: AVPlayer!
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.skView {
// Load the SKScene from 'backgroundScene.sks'
guard let scene = SKScene(fileNamed: "backgroundScene") else {
print ("Could not create a background scene")
return
}
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
// Add the video node
guard let alphaMovieURL = Bundle.main.url(forResource: "camera_city_animated", withExtension: "mov") else {
print("Failed to overlay alpha movie on the background")
return
}
videoPlayer = AVPlayer(url: alphaMovieURL)
let video = SKVideoNode(avPlayer: videoPlayer)
video.size = CGSize(width: view.frame.width, height: view.frame.height)
print( "Video size is %f x %f", video.size.width, video.size.height)
scene.addChild(video)
// Play video
videoPlayer.play()
videoPlayer?.actionAtItemEnd = .none
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: .AVPlayerItemDidPlayToEndTime,
object: videoPlayer?.currentItem)
}
}
#objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: CMTime.zero, completionHandler: nil)
}
}
}
Into a SwiftUI View by placing it inside the func makeUIView(context: Context) -> UITextView {} of my struct TransparentVideoLoop: UIViewRepresentable {} struct.
What am I missing?
Full code:
struct TransparentVideoLoop: UIViewRepresentable {
func makeUIView(context: Context) -> UITextView {
#IBOutlet var skView: SKView!
var videoPlayer: AVPlayer!
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.skView {
// Load the SKScene from 'backgroundScene.sks'
guard let scene = SKScene(fileNamed: "backgroundScene") else {
print ("Could not create a background scene")
return
}
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
// Add the video node
guard let alphaMovieURL = Bundle.main.url(forResource: "camera_city_animated", withExtension: "mov") else {
print("Failed to overlay alpha movie on the background")
return
}
videoPlayer = AVPlayer(url: alphaMovieURL)
let video = SKVideoNode(avPlayer: videoPlayer)
video.size = CGSize(width: view.frame.width, height: view.frame.height)
print( "Video size is %f x %f", video.size.width, video.size.height)
scene.addChild(video)
// Play video
videoPlayer.play()
videoPlayer?.actionAtItemEnd = .none
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: .AVPlayerItemDidPlayToEndTime,
object: videoPlayer?.currentItem)
}
}
#objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: CMTime.zero, completionHandler: nil)
}
}
}
func updateUIView(_ uiView: UITextView, context: Context) {
}
}
I have to return the view, but this is more complex than in the tutorials.
Use UIViewControllerRepresentable instead, e.g.
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
#Binding var selectedImage: UIImage?
#Environment(\.presentationMode) var presentationMode
func makeCoordinator() -> ImagePicker.Coordinator {
Coordinator()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
func makeUIViewController(context: Context) -> some UIViewController {
context.coordinator.imagePicker
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
lazy var imagePicker: UIImagePickerController = {
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
return imagePickerController
}()
var imageSelected: ((UIImage) -> Void)?
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
DispatchQueue.main.async {
if let selectedImage = (info[.editedImage] ?? info[.originalImage]) as? UIImage {
imageSelected?(selectedImage)
}
//self.parent.presentationMode.wrappedValue.dismiss()
}
}
}
}
Note this is inspired by ImagePicker.swift from an Apple sample but the developer got the Coordinator wrong so I have corrected that. It also needs the update func fixed.

Xcode SwiftUI Sharing custom image is blank

I am trying to allow my user to share an image with an overlay via text or social media.
I am successfully creating an image (I know because it shows up in the photos album) using the following extension:
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
When I ‘share’, it acknowledges the image is there (the share sheet changes its options) but the image is either not there or it is an empty white square.
I call it like this:
let myImage = textView.snapshot() //uses the snapshot extension above
ShareSheet(activityItems: ["my app name", myImage])
struct ShareSheet: UIViewControllerRepresentable {
typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
let activityItems: [Any]
let applicationActivities: [UIActivity]? = nil
let excludedActivityTypes: [UIActivity.ActivityType]? = nil ///[.postToFacebook] //nil
let callback: Callback? = nil
func makeUIViewController(context: Context) -> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: activityItems,
applicationActivities: applicationActivities)
controller.excludedActivityTypes = excludedActivityTypes
controller.completionWithItemsHandler = callback
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
// nothing to do here
}
}
I know this is not an answer, but since the comments are very limited,
I'm posting some code that may help find why it does not work for you.
For me it seems work, on macos 12.beta5, xcode 13.beta5, target ios 15 and macCatalyst. This is the code I used in my test:
import Foundation
import SwiftUI
import UIKit
import MobileCoreServices
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
#State var shareIt = false
#State var myImage = UIImage(systemName: "globe")
var body: some View {
VStack (spacing: 55) {
Image(uiImage: myImage!).resizable().frame(width: 222, height: 222)
Button(action: {
myImage = snapshot()
shareIt = true
}) {
Text("Click to share")
}
.sheet(isPresented: $shareIt, onDismiss: {shareIt = false}) {
ShareSheet(activityItems: [myImage as Any])
}
}
}
}
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
struct ShareSheet: UIViewControllerRepresentable {
typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
let activityItems: [Any]
let applicationActivities: [UIActivity]? = nil
let excludedActivityTypes: [UIActivity.ActivityType]? = nil ///[.postToFacebook] //nil
let callback: Callback? = nil
func makeUIViewController(context: Context) -> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: activityItems,
applicationActivities: applicationActivities)
controller.excludedActivityTypes = excludedActivityTypes
controller.completionWithItemsHandler = callback
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) { }
}

SwiftUI go back to RootView in sceneWillEnterForeground SceneDelegate.swift

we try to reset the current View to Root if App will enter Foreground.
How can we do that in SwiftUI?
func sceneWillEnterForeground(_ scene: UIScene) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = RootView().environment(\.managedObjectContext, context)
if let windowScene = scene as? UIWindowScene {
let singleOrder = SingleOrder()
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(singleOrder))
self.window = window
window.makeKeyAndVisible()
}
}
There is no "go back" but the possible approach is to recreate root view controller, by moving "by default" generated content creation into other delegate method as below...
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
self.window = window
}
}
func sceneWillEnterForeground(_ scene: UIScene) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = RootView().environment(\.managedObjectContext, context)
let singleOrder = SingleOrder()
window?.rootViewController = UIHostingController(rootView: contentView.environmentObject(singleOrder))
window?.makeKeyAndVisible()
}

Problem: How to call a SwiftUI View Struct Method from SceneDelegate

Currently I'm the one not getting it.
Problem:
Trying to connect to a View Method from a Scene Delegate e.g.:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
...
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
// guard let contentView = ???? as? ContentView else { return }
// contentView.callMethod(parameter: true)
}
}
struct ContentView: View {
var body: some View {
...
}
func callMethode(parameter: Bool) {
print("called")
}
}
Any clue how to connect to the View and call a method in-between?
thx
Jo
This is counter to the design of the SwiftUI framework. You should not have any persistent view around to call methods on. Instead, views are created and displayed as needed in response to your app's state changing.
For example, if your SceneDelegate had a reference to an instance of a model class, and your view depended on that model, you could modify the model in scene(_:continue:) and your view would update automatically.
If you can provide more specifics on what you're attempting to do, I may be able to give a more specific answer.
Many thanks. Fully understood... Will read the docs...
So is this a possible way as an example:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let activity = UserActivityManager()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(activity))
self.window = window
window.makeKeyAndVisible()
if let userActvity = connectionOptions.userActivities.first {
guard let title = userActvity.title else { return }
activity.doAction(action: title)
}
}
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard let title = userActivity.title else { return }
activity.doAction(action: title)
}
}

Swift 3 NavigationController segue back causes wkwebview to move into wrong position

I have a ViewController containing a WKWebView, the view is positioned correctly the first time it loads, but after moving to another view (I'm opening another view by intercepting links from the WebView) and pressing the navigation item (back button) it briefly appears in the right place, then reloads with the top of the webview behind the navigation bar so the top of the page is cut off.
class HomeVC: BaseViewController, WKNavigationDelegate, WKUIDelegate {
var webView: WKWebView?
override func viewDidAppear(_ animated: Bool) {
self.edgesForExtendedLayout = UIRectEdge.top;
super.viewDidLoad()
addSlideMenuButton()
let screenSize: CGRect = UIScreen.main.bounds
let frameRect: CGRect = CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height)
let url: NSURL = Bundle.main.url(forResource: "services", withExtension: "html")! as NSURL
let requestObj: NSURLRequest = NSURLRequest(url: url as URL);
self.webView = WKWebView(frame: frameRect)
self.webView?.load(requestObj as URLRequest)
self.webView?.navigationDelegate = self
self.webView?.uiDelegate = self
self.view = self.webView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationItem.title = ""
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.title = "SELECT A SERVICE"
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
let link: String = (webView.url?.absoluteString)!
print(link)
if(link == "file:///haircut") {
print(link)
self.openViewControllerBasedOnIdentifier("WebVC")
}
}
I've searched around and can't find any similar issues, nor can I see anything obvious in the code.
You have are calling super.viewDidLoad from func viewDidAppear(), what can cause unexpected behaviour. Therefore your UIViewController subclass will never notify its superclass, that the view has been loaded.
override func viewDidAppear(_ animated: Bool) {
// Do not do this before calling super!
self.edgesForExtendedLayout = UIRectEdge.top;
// You are calling the wrong the function for super
// It should be super.viewDidAppear(animated)
super.viewDidLoad()
addSlideMenuButton()
let screenSize: CGRect = UIScreen.main.bounds
let frameRect: CGRect = CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height)
let url: NSURL = Bundle.main.url(forResource: "services", withExtension: "html")! as NSURL
let requestObj: NSURLRequest = NSURLRequest(url: url as URL);
self.webView = WKWebView(frame: frameRect)
self.webView?.load(requestObj as URLRequest)
self.webView?.navigationDelegate = self
self.webView?.uiDelegate = self
self.view = self.webView
}