SwiftUI subview going off of the window - swiftui

As a SwiftUI beginner, I have just recently started creating my first MacOS app. However, when I was trying to implement the NSVisualEffectView to blur the background, the contentView that I was using went off of the screen, which wasn't visible at all. It looked something like this:
So I tried fixing the size of the text I had in the contentView by making the text I had into Text("Hello world!").frame(width: 700, height:500), and the screen became like this:
By doing this, in the bottom left corner, the tiny shapes of Hello world! can be seen. However, unless I position the text on the top right of the contentView, I can't seem to move it. Does anyone know how to fix this?
*For reference, here is the AppDelegate.swift contents:
import SwiftUI
#main
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
let visualEffect = NSVisualEffectView()
visualEffect.blendingMode = .behindWindow
visualEffect.state = .active
visualEffect.material = .fullScreenUI
visualEffect.addSubview(NSHostingView(rootView: contentView))
// Create the window and set the content view.
window = NSWindow(
contentRect: .zero,
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = visualEffect
window.makeKeyAndOrderFront(nil)
window.titlebarAppearsTransparent = true
window.titleVisibility = .hidden
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}

Related

SwiftUI Size to fit or word-wrap navigation title

I have a navigation title that is too large on some smaller devices.
I've read many ways to set the titleTextAttributes and largeTitleTextAttributes of the UINavigationBar.appearance() however when setting the paragraph style to word wrap, it seems to remove the standard ... clipping and have the text continue off the edge of the screen without wrapping:
init() {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping
UINavigationBar.appearance().largeTitleTextAttributes = [
.paragraphStyle: paragraphStyle
]
}
I want to maintain the SwiftUI behaviour where the title is shown as large text until the view is scrolled up and it moves to the navigation bar, so getting the .toolbar directly won't help.
I also don't want to just specify a smaller font as I only want it to shrink or wrap if necessary.
Has anyone managed to achieve this?
You can add this line on the initializer of the view where yo have the issue
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).adjustsFontSizeToFitWidth = true
Example:
struct YourView: View {
init() {
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).adjustsFontSizeToFitWidth = true
}
var body: some View {
NavigationView {
Text("Your content")
.navigationBarTitle("Very very large title to fit in screen")
}
}
}

iOS15 Xcode 13 Global Accent Color not working

I'm using the latest beta of Xcode 13 with an app for iOS 14 and now I'm facing this strange issue:
The global accent color of my app was working fine until the iOS 15 update when the color is now set as the default blue where before it was my custom color.
Here is the asset catalog:
This is my project settings page where you can see that the accent color is correct.
And this is what the app looks like when built. The color is the default blue when it needs to be a really dark blue/purple color.
We were using the UIAppearance API in our app. We were not setting the tint color, but somehow calling any of the UIAppearance API's after the app has finished launching causes this behavior.
enum AppAppearance {
static func configure() {
configureCustomBarApperance()
UITableView.appearance().backgroundColor = UIColor(named: .primaryBackground)
UITextView.appearance().backgroundColor = nil
UIScrollView.appearance().keyboardDismissMode = .interactive
}
static func configureCustomBarApperance() {
let barAppearance = UIBarAppearance()
barAppearance.configureWithTransparentBackground()
barAppearance.backgroundColor = UIColor(named: .primaryBackground)
// Toolbars
let toolbarAppearance = UIToolbarAppearance(barAppearance: barAppearance)
UIToolbar.appearance().standardAppearance = toolbarAppearance
UIToolbar.appearance().compactAppearance = toolbarAppearance
UIToolbar.appearance().scrollEdgeAppearance = toolbarAppearance
// Navigation Bars
let navBarAppearance = UINavigationBarAppearance(barAppearance: barAppearance)
navBarAppearance.titleTextAttributes[.foregroundColor] = UIColor.secondaryLabel
UINavigationBar.appearance().standardAppearance = navBarAppearance
UINavigationBar.appearance().compactAppearance = navBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
// Tab Bars
let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithTransparentBackground()
tabBarAppearance.backgroundColor = UIColor(named: .secondaryBackground)
UITabBar.appearance().standardAppearance = tabBarAppearance
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
}
}
Our solution was to move all of the UIAppearance API work to the initializer of the AppDelegate and this fixed the issue for us. So instead of calling AppAppearance.configure() after the app finished launching...we call it from AppDelegate.init and our Global Accent Color is now being honored.
Don't ask me why...I couldn't tell you.
I finally found a temporary workaround on this AppleDeveloperForum Thread
credit to: #chad_sykes for this answer
I found an alternate solution, which was to set the .accentColor on the main view in the WindowGroup and it gets used across the app.
#main
struct CurvApp: App {
var body: some Scene {
WindowGroup {
myMainView
.accentColor(CurvGlobalAppearance.curvAccentColor)
}
}
}

How do I hide default CommandMenu in SwiftUI on macOS?

I'm (attempting) switching over my AppDelegate macOS app to the SwiftUI lifecycle - but can't seem to find out how to handle the CommandMenu. I just want to delete these default menu items (Fie, Edit, View, etc...). In the past, I would just delete them from the Storyboard - but I'm not using a storyboard here. Is there a way to delete these items in SwiftUI?
The items I want to delete:
I know how to add new items via:
.commands {
MyAppMenus()
}
But that just adds them inline with the existing menu items.
swiftUI -- override AppDelegate with your custom:
#main
struct PixieApp: App {
#NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
///........
}
code of appDelegate:
final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationWillUpdate(_ notification: Notification) {
if let menu = NSApplication.shared.mainMenu {
menu.items.removeFirst{ $0.title == "Edit" }
menu.items.removeFirst{ $0.title == "File" }
menu.items.removeFirst{ $0.title == "Window" }
menu.items.removeFirst{ $0.title == "View" }
}
}
}
result:
Until SwiftUI adds more support for adjusting menus, I think you have to worry about SwiftUI reseting the NSApp.mainMenu whenever it updates a window.body. I haven't tried every method for adjusting the mainMenu, but of the methods I tried, the flaw was that SwiftUI seems to have no check for whether it last set NSApp.mainMenu or if something else did.
So however you are managing the menu, update it after SwiftUI has.
Use KVO and watch the NSApp for changes on .mainMenu. Then make your changes with a xib, or reseting the whole thing, or editing SwiftUI's menus.
Example:
#objc
class AppDelegate: NSObject, NSApplicationDelegate {
var token: NSKeyValueObservation?
func applicationDidFinishLaunching(_ notification: Notification) {
// Adjust a menu initially
if let m = NSApp.mainMenu?.item(withTitle: "Edit") {
NSApp.mainMenu?.removeItem(m)
}
// Must refresh after every time SwiftUI re adds
token = NSApp.observe(\.mainMenu, options: .new) { (app, change) in
// Refresh your changes
guard let menu = app.mainMenu?.item(withTitle: "Edit") else { return }
app.mainMenu?.removeItem(menu)
}
}
}
struct MarblesApp: App {
#NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some View {
//...
}
}
This seems to work in Xcode 13.4.1 with Swift 5 targeting macOS 12.3.
Hopefully Apple adds greater control soon. It seems Catalyst has other options. Or you can create a traditional AppKit app and insert the SwiftUI views into it.
You can remove command menu items through the AppDelegate file:
override func buildMenu(with builder: UIMenuBuilder) {
super.buildMenu(with: builder)
builder.remove(menu: .services)
builder.remove(menu: .format)
builder.remove(menu: .toolbar)
}
This thread on the Apple Developer forum might help as well: https://developer.apple.com/forums/thread/649096
CommandGroup(replacing: CommandGroupPlacement.appVisibility, addition: {})

In Swift Spritekit, whenever I transition from one Scene to another, my sks file is not transfering?

Basically I have a MenuScene and a GameScene for my game. The MenuScene is first as shown in the viewDidLoad function from ViewController
override func viewDidLoad() {
super.viewDidLoad()
if let scene = MenuScene(fileNamed:"MenuScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .aspectFit
skView.presentScene(scene)
}
}
There is a simple menu screen with a play button that transfers right into the game. Here is the if-block in my touchesBegan method:
if node == playButton {
playButton.size.height=playButton.size.height+30
playButton.size.width=playButton.size.height+30
if view != nil {
let transition:SKTransition = SKTransition.fade(withDuration: 1)
let scene:SKScene = GameScene(size: self.size)
self.view?.presentScene(scene, transition: transition)
}
}
But when it transitions into my GameScene, the sks files for my GameScene doesn't render? And all the variables in my GameScene.swift that connects to the sprites in the sks file are missing. How do I fix this?
Try: let scene = SKScene(fileNamed: "GameScene")
let scene:SKScene = GameScene(size: self.size) means that you are loading an instance of type GameScene. At no point will it load any file.
SKScene(fileNamed: "GameScene") on the other hand will load a file, just be sure to check that the custom class field on your sks file says GameScene

mouseMoved function not called when I move the mouse?

I am trying to find mouse coordinates within an SKScene, however, the moveMouse function is not being called. (This is in a Swift Playground by the way) I even wrote a print function that tested to see if the function was even being called, but it prints absolutely nothing.
This is how I set up my NSTrackingArea:
let options = [NSTrackingAreaOptions.mouseMoved, NSTrackingAreaOptions.activeInKeyWindow, NSTrackingAreaOptions.activeAlways, NSTrackingAreaOptions.inVisibleRect, ] as NSTrackingAreaOptions
let tracker = NSTrackingArea(rect: viewFrame, options: options, owner: self.view, userInfo: nil)
self.view?.addTrackingArea(tracker)
And here is the mouseMoved function (the one that is not being called)
override public func mouseMoved(with event: NSEvent) {
point = event.location(in: self)
print(point)
}
Is there a reason that mouseMoved isn't being called?
I created a playground with the following code (and only that code):
import AppKit
import SpriteKit
import PlaygroundSupport
class Scene:SKScene {
override public func mouseMoved(with event: NSEvent) {
let point = event.location(in: self)
print(point)
}
}
let frame = CGRect(x:0, y:0, width:1920, height:1080)
let view = SKView(frame:frame)
let scene = Scene(size: CGSize(width: 1080, height: 1080))
scene.backgroundColor = #colorLiteral(red: 0.4078431373, green: 0.7843137255, blue: 0.6509803922, alpha: 1)
scene.scaleMode = .aspectFit
let options = [NSTrackingAreaOptions.mouseMoved, NSTrackingAreaOptions.activeInKeyWindow, NSTrackingAreaOptions.activeAlways, NSTrackingAreaOptions.inVisibleRect, ] as NSTrackingAreaOptions
let tracker = NSTrackingArea(rect:frame, options: options, owner:view, userInfo: nil)
view.addTrackingArea(tracker)
PlaygroundPage.current.needsIndefiniteExecution = true
view.presentScene(scene)
PlaygroundPage.current.liveView = view
Then, I opened the playground Timeline view by clicking the "Show the Assistant Editor" button in the toolbar. I also opened the Debug area so that I could see the console.
At that point, the Timeline view showed a green view. I moved my mouse pointer over the green view and I could see the mouse coordinates being printed out in the console. So, as far as I can tell, the above code works fine.
Could you please try the code at your end and see what happens?