Working on a macOS app. I need to display user-added images as a background to the view, with all of:
Tiling (auto repeat in both axes)
Scaling (user-configured scale of the image)
Offset (user-configured offset)
I've been able to achieve scaling and offset with:
Image(nsImage: nsImage)
.scaleEffect(mapImage.scale)
.offset(mapImage.offset)
.frame(width: scaledImageSize.width, height: scaledImageSize.height)
But when I try to incorporate tiling into that with .resizable(resizingMode: .tile) everything breaks.
Is there a way to position the "anchor" of an image, scale it, and tile it in both axes to fill a container view?
Post
Replies
Boosts
Views
Activity
I've got a Mac Document App using SwiftUI and SwiftData.
All is working well with the models editing, etc.
There's a feature I need to implement, and can't seem to make it work.
From the main window of the app, I need to be able to launch an auxilliary window containing a view-only representation of the model being edited. The required workflow is something like this:
Open a document (SwiftData)
Select a sub-model of the document
Launch the aux window to display the view of the model data (must be in a separate window, because it will be on a different physical display)
Continue making edits to the sub-model, as they are reflected in the other window
So, below is the closest I've been able to come, and it's still not working at all. What happens with this code:
Click on the "Present" button, the encounter-presentation Window opens, but never loads the data model or the view. It's just an empty window.
This is the spot in the main view where the auxiliary window will be launched:
@State
var presenting: Presentation? = nil
var presentingThisEncounter: Bool {
presenting?.encounter.id == encounter.id
}
@Environment(\.openWindow) var openWindow
...
if presentingThisEncounter {
Button(action: { presenting = nil }) {
Label("Stop", systemImage: "stop.fill")
.padding(.horizontal, 4)
}
.preference(key: PresentationPreferenceKey.self, value: presenting)
} else {
Button(action: {
presenting = Presentation(encounter: encounter, display: activeDisplay)
openWindow(id: "encounter-presentation")
}) {
Label("Present", systemImage: "play.fill")
.padding(.horizontal, 4)
}
.preference(key: PresentationPreferenceKey.self, value: nil)
}
Presentation is declared as:
class Presentation: Observable, Equatable {
Here's the contents of the App, where the DocumentGroup & model is instantiated, and the aux window is managed:
@State
var presentation: Presentation?
var body: some Scene {
DocumentGroup(editing: .encounterList, migrationPlan: EncounterListMigrationPlan.self) {
ContentView()
.onPreferenceChange(PresentationPreferenceKey.self) { self.presentation = $0 }
}
Window("Presentation", id: "encounter-presentation") {
VStack {
if let presentation = presentation {
PresentingView(presentation: presentation)
}
}
}
}
And the definition of PresentationPreferenceKey:
struct PresentationPreferenceKey: PreferenceKey {
static var defaultValue: Presentation?
static func reduce(value: inout Presentation?, nextValue: () -> Presentation?) {
value = nextValue()
}
}
One of the requirements for an app I'm working on is for the user to be able to choose from a handful of "themes" for their experience. A "theme" in this context is pretty simple:
An accentColor
A background color (for lists, scroll views, etc)
The intent is for the theme to be applied in inverse to the Navigation Bar, so if the accentColor is a dark blue, the background of the nav bar is dark blue, and the (much lighter blue) foreground color is used as the tint color of the nav bar contents.
I know how to apply these styles appropriately to all the app elements involved. Where I'm having trouble is: When the user goes into settings (within the app) and chooses a new theme, how can I get the new theme to propagate to all existing NavigationViews (including the main "root" navigation, which is "beneath" the settings modal)?
I thought maybe I could use a preference key to bubble the theme choice all the way up to the root view, set the theme, then propagate it back down with an environment key, but I've either got something wrong with the relevant incantation, or that just isn't going to play nice with the appearance() methods necessary to set the NavigationBar background.
Has anyone accomplished something like this in a SwiftUI app?
I'm working on an app using the new SwiftUI App lifecycle for iOS and need to pop up a PIN entry privacy screen when the app is backgrounded.
I've hooked into the ScenePhase and can catch the right moment and raise the screen over the main UI. But if the user was in any of the half a dozen modals (my SettingsView is a fullScreenCover, several other data entry views are sheets), then the PIN screen pops up underneath these modals.
This was always a bit of a dodgy thing to accomplish in UIKIt (walking the UIViewController graph to find the "top-most" to present from), but is it even possible in SwiftUI?
I've tried everything I can think of, even swapping out the root contents of the WindowGroup, but it seems that modally presented views are in separate system-managed window groups?
Has anyone done this successfully? or am I tilting at windmills at this point?
I'm scratching my head trying to figure out how to use the new Compositional layout approach to achieve a layout like the following:+-----------+ +--------------------------+
| section 0 | | section 2 |
|-----------| |--------------------------|
| | | |
|-----------| |--------------------------|
| | | |
|-----------| |--------------------------|
| | | |
+-----------+ |--------------------------|
| |
+-----------+ |--------------------------|
| section 1 | | |
|-----------| |--------------------------|
| | | |
|-----------| +--------------------------+
| |
|-----------| +--------------------------+
| | | section 3 |
+-----------+ |--------------------------|
| |
|--------------------------|
| |
+--------------------------+It seems that sections can only flow top to bottom? Am I stuck using the old approach of building a custom layout manually? Or am I missing something?
In the 2016 WWDC session "Crafting Modern Cocoa Apps", there is a description of how to set the offset of a window's content when using the "Full Size Content View" setting:override func updateViewConstraints() {
if topConstraint == nil {
if let topAnchor = titleTextField.window?.contentLayoutGuide?.topAnchor {
topConstraint = titleTextField.topAnchor.constraint(equalTo: topAnchor, constant: 2)
topConstraint!.isActive = true
}
}
super.updateViewConstraints()
}However, in Xcode 8.1 [editted], this does not compile, because NSWindow's contentLayoutGuide property is declared as open var contentLayoutGuide: Any? { get }Has anyone successfully used this property? If so, how? Did you have to cast the Any? type to something concrete, and if so, what?