In iOS 17.1 (and 17.2 beta), the arrowEdge parameter of the SwiftUI popoverTip doesn't work anymore.
This code
button
.popoverTip(tip, arrowEdge: .bottom)
looks like this on iOS 17.0
and like this on 17.1 and up.
I checked permittedArrowDirections of the corresponding UIPopoverPresentationController (via the Memory Graph): It's .down on iOS 17.0 and .any (the default) on 17.1. It seems the parameter of popoverTip is not properly propagated to the popover controller anymore.
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
I have gone through several tutorials for WeatherKit. But my sample app doesn't return weather data. The following is a list of what I have.
I've registered a Bundle ID for my sample app with the WeatherKit capability on.
I've created a developer profile for my sample app.
I've opened my Xcode project to make sure that the WeatherKit capability is enabled.
I have run my sample app with an actual device.
I have waited for more than 30 minutes for the service to kick in. It's been several days.
The following is my code.
import SwiftUI
import CoreLocation
import WeatherKit
struct ContentView: View {
@State var currentWeather: CurrentWeather?
var body: some View {
NavigationStack {
List {
Group {
SampleCell(title: "Temperature", value: String(currentWeather?.apparentTemperature.value ?? 0.0) + "℃")
SampleCell(title: "Cloud coverage", value: String(currentWeather?.cloudCover ?? 0.0))
SampleCell(title: "Weather condition", value: String(currentWeather?.condition.description ?? ""))
SampleCell(title: "Dew point", value: String(currentWeather?.dewPoint.value ?? 0.0) + "℃")
SampleCell(title: "Humidity", value: String(currentWeather?.humidity ?? 0.0))
SampleCell(title: "Pressure", value: String(currentWeather?.pressure.value ?? 0.0) + "mbar")
SampleCell(title: "Pressure trend", value: String(currentWeather?.pressureTrend.description ?? ""))
SampleCell(title: "Temperature", value: String(currentWeather?.temperature.value ?? 0.0) + "℃")
SampleCell(title: "UV index", value: String(currentWeather?.uvIndex.value ?? 0))
SampleCell(title: "Visibility", value: String(currentWeather?.visibility.value ?? 0.0) + "m")
}
SampleCell(title: "Window direction", value: String(currentWeather?.wind.direction.value ?? 0.0) + "°")
SampleCell(title: "Window speed", value: String(currentWeather?.wind.speed.value ?? 0.0) + "km/h")
SampleCell(title: "Gust", value: String(currentWeather?.wind.gust?.value ?? 0.0) + "km/h")
}
.navigationTitle(Text("CurrentWeather"))
.task {
let service = WeatherService()
let location = CLLocation(
latitude: 35.467081,
longitude: 139.620798
)
do {
let weather = try await service.weather(for: location)
currentWeather = weather.currentWeather
} catch let error {
print(error.localizedDescription)
}
}
}
}
}
struct SampleCell: View {
var title: String
var value: String
var body: some View {
VStack {
HStack {
Text(title)
Spacer()
Text(value)
}
}
}
}
Yet, I constantly get the following warnings.
2023-11-29 09:33:46.504737+0900 WeatherCrazyMama[15279:9734572] [WeatherDataService] Aborting silent interpolation: no interpolator object; location=CLLocationCoordinate2D(latitude: 35.467081, longitude: 139.620798)
2023-11-29 09:33:47.900605+0900 WeatherCrazyMama[15279:9734577] [AuthService] Failed to generate jwt token for: com.apple.weatherkit.authservice with error: Error Domain=WeatherDaemon.WDSJWTAuthenticatorServiceListener.Errors Code=2 "(null)"
2023-11-29 09:33:47.989603+0900 WeatherCrazyMama[15279:9734572] [WeatherService] Encountered an error when fetching weather data subset; location=<+35.46708100,+139.62079800> +/- 0.00m (speed -1.00 mps / course -1.00) @ 2023/11/29 9:33:46 AM Japan Standard Time, error=WeatherDaemon.WDSJWTAuthenticatorServiceListener.Errors 2 Error Domain=WeatherDaemon.WDSJWTAuthenticatorServiceListener.Errors Code=2 "(null)"
The operation couldn’t be completed. (WeatherDaemon.WDSJWTAuthenticatorServiceListener.Errors error 2.)
What am I doing wrong? Thanks.
I have something that looks like:
NavigationStack {
List(self.items, id: \.self, selection: self.$selectedItems) { item in
NavigationLink {
ItemView(item: item)
.environment(\.managedObjectContext, self.viewContext)
} label: {
LabelWithMenuView(object: item) { ptr in
self.labelHandler(item: item, newName: ptr)
}
}
}
if self.editMode?.wrappedValue == .active {
editButtons
} else {
TextField("Add Item", text: self.$newItem)
.onSubmit {
self.addItem()
self.newItem = ""
}
.padding()
}
}
#if os(iOS)
.toolbar {
EditButton()
}
.onChange(of: self.editMode?.wrappedValue) { old, new in
print("editMode \(old) -> \(new)")
}
#endif
With that layout, the edit button doesn't show up at all; if I put it as part of the List, it does show up, but the first click doesn't do anything; after that, it works, but the onChange handler doesn't show it getting changed, and the editButtons don't go away.
Xcode 15.0.1, iOS 17.0.2
Problem: when drawing a shader in Dark mode, a SwiftUI shader does not use the alpha channel correctly.
This behaviour occurs even if the shader sets the alpha value to 0.
This occurs in the Xcode Canvas (Preview), the Xcode simulator and on device.
This behaviour occurs regardless of whether the original or new color is a Dynamic Color (e.g. .orange) or an absolute color (e.g. a hex value.)
Further... if I force-quit the app, set the device to Dark Mode, and relaunch the app, I very briefly see the desired result -- the globe shows on a dark background perhaps for 1 frame -- but then the globe is quickly replaced by a solidly filled rectangle (the globe's frame).
// SwiftUI view
struct ContentView: View {
var body: some View {
Image(systemName: "globe")
.font(.system(size: 200))
.foregroundStyle(.blue)
.colorEffect(
ShaderLibrary.replaceColor(.color(Color(hex: "ff8000")))
)
}
}
// Shader code
#include <metal_stdlib>
using namespace metal;
/// Given an existing color, replace it with a new color, but using the old color's alpha.
[[stitchable]] half4 replaceColor(float2 pos, half4 oldColor, half4 newColor) {
return half4(newColor.r, newColor.g, newColor.b, oldColor.a);
}
Question: what am I missing here? My guess is that it has something to do with the alpha premultiplication. But how to fix this? Thank you for any help.
Light Mode
Dark Mode
I created a SwiftChart as below and I would like to have two YAxis, one for amount and the second for count. So, the amount YAxis is a different scale then the count YAxis.
Does anybody have an example of this or shed some light on coding two different YAxis?
Thanks
ForEach(seriesArt) { series in
ForEach(series.chartSeries.chartEntry) {
BarMark(
x: .value("Tier", $0.tier),
y: .value("Price", $0.keyValue)
)
}
.foregroundStyle(by: .value("Count", series.chartCategory))
.position(by: .value("Price", series.chartCategory))
}
}
.frame(width: 400, height: 200)
.chartXAxis {
AxisMarks(position: .bottom, values: .automatic) {
AxisValueLabel()
.foregroundStyle(Color.white)
}
}
.chartYAxis {
AxisMarks(position: .leading, values: .automatic) { value in
AxisGridLine(centered: true, stroke: StrokeStyle(lineWidth: 1))
AxisValueLabel() {
if let intValue = value.as(Int.self) {
Text("\(intValue)")
.font(.system(size: 10))
.foregroundColor(.white)
}
}
}
.chartYAixs - for count sum by tier which needs to be a different scale from the amount YAxis
}
}
}
Platform
Late 2018 Intel 15” MacBook Pro
macOS 15
Xcode 15, CLTools 15
Fault Message
This fault message started after upgrading to macOS Sonoma 15. It only occurs on macOS targeted projects (not iOS). The fault occurs on projects using the TextField or TextEditor views. The projects build, test and run fine.
FAULT: <NSRemoteView: 0x7f7f8ec34fe0 com.apple.TextInputUI.xpc.CursorUIViewService TUICursorUIViewService> determined it was necessary to configure <TUINSWindow: 0x7f7f8e91b490> to support remote view vibrancy
Example
The fault does not occur in UI Tests
ContentView.txt
FaultTestUITests.txt
I have a scroll view that scrolls horizontally and one of my users is asking that it respond to their scroll wheel without them having to use the shift key. Is there some way to do this natively? If not, how can I listen for the scroll wheel events in swiftUI and make my scroll wheel scroll to respond to them?
Hi
How can we add multi view to NavigationPath in one go like in code below maybe using something like path.append(mobiles.all) ? where all 3 views of mobiles Array get added at once ?
Kindest Regards
`struct NavigationPathView: View {
var mobiles: [Mobiles] = [.init (name: "iPhone", imageName: "folder", color: .mint),
.init (name: "Samsung", imageName: "pin", color: .pink),
.init (name: "Mac Pro", imageName: "book", color: .gray)]
@State var path = NavigationPath()
var body: some View {
NavigationStack (path: $path) {
List {
Section ("Platforms") {
Button("Go To Platform") {
path.append(mobiles.first!)
}
Button("Go To Mobile") {
path.append(mobiles.last!)
}
Button("Add All Mobiles") {
}
}
}
}
.navigationDestination(for: Mobiles.self) {mobile in
ZStack {
mobile.color.ignoresSafeArea()
VStack {
Image(systemName: mobile.imageName)
Label (mobile.name, systemImage: mobile.imageName)
.font(.largeTitle).bold().foregroundColor(.white)
Button("Go Home") {
path.removeLast(path.count)
}
}
}
}
}
}
Hey Everyone,
I've been remaking an app using SwiftUI, and I am running into a weird animation bug/hitch using .sheet(isPresented:).
I have a GIF illustrating the issue, but can't seem to attach it to this forum post. Let me know if there's anyway to share this.
Link to gif detailing issue(self hosted)
Regardless, my issue:
To describe the issue: I'm using two Detents for the sheet, [.medium, .large]. I have a TextField that's displayed in the sheet, and when opening it, the keyboard moves my content upward (expected, working fine).
My issue comes when I programmatically resign the TextField (using .focused($isFocused). The content on the sheet jumps up, beyond the upper bound of the sheet. My hypothesis is that the sheet's content is immediately redrawn, using the medium detent frame, but before the animation has finished going from large to medium.
It's possible this is not a SwiftUI bug, but something wrong with my implementation. I'll provide the relevant code below. Any help is greatly appreciated!
Onboarding.swift (presents the sheet)
@ViewBuilder
var content: some View {
VStack {
headline
.foregroundStyle(.white.opacity(0.95))
subHeadline
.foregroundStyle(.white)
Spacer()
messages
.foregroundStyle(.white)
Spacer()
callToAction
}
.ignoresSafeArea(.keyboard)
.sheet(isPresented: $showJoin) {
join
}
}
var join: some View {
Join()
.ignoresSafeArea()
.presentationCornerRadius(40)
.presentationDragIndicator(.hidden)
.presentationBackground {
Rectangle()
.fill(.ultraThinMaterial)
.padding(.bottom, -1000)
}
.presentationDetents([.medium, .large])
}
Join.swift (holds the sheet's content, and displays the heading)
struct Join: View {
@State private var didSignUp = false
var body: some View {
VStack {
heading
Divider()
contentView
}
.animation(.easeInOut, value: didSignUp)
.transition(.opacity)
.interactiveDismissDisabled(didSignUp)
}
var heading: some View {
VStack(spacing: 8) {
Text(didSignUp ? "Verify" : "Start here")
.frame(maxWidth : .infinity, alignment: .leading)
.font(.largeTitle.bold())
.foregroundColor(.primary)
.blendMode(.overlay)
Text(didSignUp ? "Enter code" : "Create an account")
.frame(maxWidth : .infinity, alignment: .leading)
.font(.callout)
.foregroundColor(.primary)
.blendMode(.overlay)
}
.padding(.top, 20)
.padding(.horizontal)
}
var contentView: some View {
Group {
if didSignUp {
Verification()
.transition(.move(edge: .trailing).combined(with: .opacity))
} else {
SignUp(didSignUp: $didSignUp)
.transition(.move(edge: .leading).combined(with: .opacity))
}
}
.padding(.horizontal)
}
}
SignUp.swift (the sheet content)
struct SignUp: View {
@Binding var didSignUp: Bool
@State private var phoneNumber: String = ""
@State private var sendingTextMessage = false
@FocusState private var isFocused: Bool
private let notice =
"""
By creating an account, you agree to our **[Terms of Service](https://cordia.app/tos)** and **[Privacy Policy](https://cordia.app/privacy)**
"""
var body: some View {
VStack {
VStack {
phoneNumberLabel
phoneNumberInput
}
.padding()
if sendingTextMessage {
LoadingIndicator(isVisible: $sendingTextMessage)
.padding()
} else {
continueButton
.padding()
}
Spacer()
termsAndConditions
.padding(.bottom)
}
}
var phoneNumberLabel: some View {
Text("Enter your phone number")
.font(.title3)
.foregroundColor(.primary)
.blendMode(.overlay)
.frame(maxWidth: .infinity, alignment: .leading)
}
var phoneNumberInput: some View {
iPhoneNumberField("(***) 867-5309", text: $phoneNumber)
.maximumDigits(10)
.formatted()
.clearsOnEditingBegan(true)
.clearButtonMode(.always)
.font(.system(size: 25, weight: .semibold))
.padding()
.glass(cornerRadius: 10)
.focused($isFocused)
}
var termsAndConditions: some View {
Text(addPolicyLinks() ?? AttributedString(notice))
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
.font(.body)
.blendMode(.overlay)
.padding()
}
var continueButton: some View {
Button(action: {
guard !sendingTextMessage else { return }
sendingTextMessage = true
isFocused = false
UIImpactFeedbackGenerator(style: .rigid).impactOccurred()
Auth.signUp(with: phoneNumber) { user, error in
didSignUp = true
}
}) {
Text("Join Cordia")
.font(.system(size: 25, weight: .semibold))
.foregroundColor(.primary.opacity(0.8))
.frame(width: 200, height: 60)
.tintedGlass(
cornerRadius: 20,
strokeColor: .cordiaGoldDark,
strokeSize: 1.0,
tint: .cordiaGold
)
}
}
private func addPolicyLinks() -> AttributedString? {
var string = try? AttributedString(markdown: notice)
if let range = string?.range(of: "Terms of Service") {
string?[range].foregroundColor = .cordiaGold
}
if let range = string?.range(of: "Privacy Policy") {
string?[range].foregroundColor = .cordiaGold
}
return string
}
}
I'm running into a confusing difference in the way SpatialTapGesture locations are handled when the targeted entities are children of AnchorEntities.
Context: I'm wanting to create entities at points on interactable entities where the user taps.
The following code snippet works fine with non-anchored entities, but produces incorrect coordinates.
func convertLocation(_ value: EntityTargetValue<SpatialTapGesture.Value>) -> SIMD3<Float> {
return value.convert(value.location3D, from: .local, to: .scene)
}
func handleTap(_ value: EntityTargetValue<SpatialTapGesture.Value>, material: SimpleMaterial) {
let location3D = convertLocation(value)
let tap = createSphereEntity(0.01, material: material, interactable: false)
tap.position = location3D
value.entity.addChild(tap, preservingWorldTransform: true)
}
and, for reference, this is the gesture modifier attached to my RealityView:
.gesture(SpatialTapGesture().targetedToAnyEntity().onEnded({ value in
let material = SimpleMaterial(color: .systemPink, roughness: 0.1, isMetallic: true)
handleTap(value, material: material)
}))
I've tried numerous combinations... .local, .global, .named for CoordinateSpace, .scene, anchor name, entity name etc for SceneRealityCoordinateSpace... preserving world transform and not, adding the new entity to the tapped entity, directly to the anchor, etc.
anyone have any ideas?
also, i noticed that in the docs for NamedCoordinateSpace it mentions
static var immersiveSpace: NamedCoordinateSpace
but no such static property exists for me?
cheers,
Mike
I recently changed my old ancient MacBookPro for a MBAir 15" M2., mainly to upgrade Xcode and Swift to SwiftUI.
But using Xcode 15.1, trying to update the preview, or trying to run the code I get bombarded by these warnings:
managedappdistributiond unexpectedly quit
findmylocated unexpectedly quit
aegirposter unexpectedly quit.
The warnings are so frequent that it stops me programming.
Mind, I'm not a professional programmer, rather a very driven amateur, but I don't think this this is what is meant by offering a great API.
Can someone please help me get rid off all this irritating stuff.
I have an immersive space
ImmersiveSpace(id: "FlappyImmersiveSpace") {
FlappySpace()
}
On this immersive space, I want to add a gesture recognizer, so that no matter where you are looking in the immersive space, I can detect a pinch.
ImmersiveSpace(id: "FlappyImmersiveSpace") {
FlappySpace()
.gesture(
TapGesture()
.onEnded({ _ in
print("TAPPED")
})
)
}
This doesn't work.
struct FlappySpace: View {
var body: some View {
RealityView { content in
// Add content
}
.gesture(
TapGesture()
.onEnded({ _ in
print("TAPPED")
})
)
}
}
Neither does this.
Does any one know how to detect gestures in an immersive space, the gesture can not be specific to an entity in the space, but any where in the entire space.
I have a Watch companion app. The SwiftUI previews fail to build for the watch app. The errors indicate it is trying to build Swift package targets that are meant for iOS only, not the watch. The watch does not include these dependencies so it is perplexing it will try to build them.
Digging into the scheme for the watch app, it includes the iOS target as you can see the screenshot. This seems to be the default when you create a new watch target. If I uncheck the boxes for the iOS target, previews will build fine. But I think this means that each time I build my Watch target, it will not simultaneously build my iOS target. I'm not sure of the impact of that.
Is this a known limitation with Previews? Is there another workaround?
Trying to use new Swift @Observable to monitor GPS position within SwiftUI content view. But how do I tie the latest locations to the SwiftUI Map's mapCameraPosition?
Well ideally the answer could cover:
How to fix this error - So get map tracking along with the User Position, but also
How to include facility to turn on/off the map moving to track the user position (which I'll need to do next). So could be tracking, then disable, move map around and have a look at things, then click button to start syncing the mapcameraposition to the GPS location again
Refer to error I'm embedded in the code below.
import SwiftUI
import MapKit
@Observable
final class NewLocationManager : NSObject, CLLocationManagerDelegate {
var location: CLLocation? = nil
private let locationManager = CLLocationManager()
func startCurrentLocationUpdates() async throws {
if locationManager.authorizationStatus == .notDetermined {
locationManager.requestWhenInUseAuthorization()
}
for try await locationUpdate in CLLocationUpdate.liveUpdates() {
guard let location = locationUpdate.location else { return }
self.location = location
}
}
}
struct ContentView: View {
var newlocationManager = NewLocationManager()
@State private var cameraPosition: MapCameraPosition = .region(MKCoordinateRegion(
center: newlocationManager.location?.coordinate ?? <#default value#>,
span: MKCoordinateSpan(latitudeDelta: 0.25, longitudeDelta: 0.25)
))
// GET ERROR: Cannot use instance member 'newlocationManager' within property initializer; property initializers run before 'self' is available
var body: some View {
ZStack {
Map(position: $cameraPosition)
Text("New location manager: \(newlocationManager.location?.description ?? "NIL" )") // works
}
.task {
try? await newlocationManager.startCurrentLocationUpdates()
}
}
}
#Preview {
ContentView()
}
I get this error while migrating from ObservableObject to @Observable.
Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
My original code:
struct SomeView: View {
@StateObject private var viewModel = ViewModel()
}
After migration:
@MainActor @Observable class BaseViewModel {
}
@MainActor class ViewModel: BaseViewModel {
}
struct SomeView: View {
@State private var viewModel = ViewModel()
}
As discussed here. It seems like @StateObject is adding @MainActor compliance to my View under the hood because it's wrappedValue and projectedValue properties are marked as @MainActor, while on @State they are not.
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
@frozen @propertyWrapper public struct StateObject<ObjectType> : DynamicProperty where ObjectType : ObservableObject {
...
@MainActor public var wrappedValue: ObjectType { get }
....
@MainActor public var projectedValue: ObservedObject<ObjectType>.Wrapper { get }
}
One solution for this is to mark my View explicitly as @MainActor struct ViewModel: View but it have it side effects, for example code like:
Button(action: resendButtonAction) {
Text(resendButtonAttributedTitle())
}
Will result a warning
Converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'
While could be easily solved by using instead
Button(action: { resendButtonAction() } ) {
Text(resendButtonAttributedTitle())
}
I still feel like marking the whole View explicitly as @MainActor is not a good practice.
Adding fake @StateObject property to my view also do the trick, but it's a hack (the same for @FetchRequest).
Can anyone think of a more robust solution for this?
I tried building the View from this section, but when there is a List on the second tab, the animation performed by the matchedGeometryEffect does not work as intended.
This video shows how the transition works with Text("Second Tab") as the second tab. Everything looks fine.
But when I replace the Text with a List, the transition flickers and does not look smooth anymore.
List {
Text("The Scarlet Letter")
Text("Moby-****")
Text("Little Women")
Text("Adventures of ")
}
Here is the code for the app.
import SwiftUI
@main
struct MyWatchApp: App {
@Namespace var library
@State var pageNumber = 0
private let bookIcon = "bookIcon"
var body: some Scene {
WindowGroup {
NavigationStack {
TabView(selection: $pageNumber) {
VStack {
Image(systemName: "books.vertical.fill")
.imageScale(.large)
.matchedGeometryEffect(
id: bookIcon,
in: library,
properties: .frame,
isSource: pageNumber == 0)
Text("Books")
}
.tag(0)
Text("Second Tab").tag(1)
}
.tabViewStyle(.verticalPage)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Image(systemName: "books.vertical.fill")
.matchedGeometryEffect(
id: bookIcon,
in: library,
properties: .frame,
isSource: pageNumber != 0)
}
}
}
}
}
}
try Tips.resetDatastore()
try Tips.configure(
[
// Reset which tips have been shown and what parameters have been tracked, useful during testing and for this sample project
.datastoreLocation(.applicationDefault),
// When should the tips be presented? If you use .immediate, they'll all be presented whenever a screen with a tip appears.
// You can adjust this on per tip level as well
.displayFrequency(.immediate)
]
)
struct UserTip: Tip {
static let hoverEvent: Event = Event(id: "hoverEvent")
@Parameter var isHovering: Bool = false
static var tipCountKey = "UserTipCount"
var title: Text
var message: Text?
var image: Image?
var tipShownLimit: Int
var options: [Option] {
// Show this tip 5 times.
[
Tips.MaxDisplayCount(5),
Tips.IgnoresDisplayFrequency(true)
]
}
var rules: [Rule] {
#Rule($isHovering) {
$0 == true
}
}
}
struct ShowPopoverTip: View {
@State private var tip = UserTip(
title: Text("the title"),
message: Text("the message here"),
image: Image(systemName: "volleyball.fill"),
tipShownLimit: 10
)
var body: some View {
Button(action: {
}) {
Text("Hover over me")
}
.popoverTip(tip)
.onAppear {
}
.onHover { hovering in
if hovering {
tip.isHovering = true
print("tip.status: \(tip.status)")
print("tip.isHovering: \(tip.isHovering)")
print("tip.shouldDisplay: \(tip.shouldDisplay)")
}else{
tip.isHovering = false
print("tip.isHovering: \(tip.isHovering)")
}
}
}
}
The popover only works once, even though I have set it to Tips.MaxDisplayCount(5)
Either the Tip is getting invalidated or popovers only show once.
debug output:
tip.isHovering: true
tip.shouldDisplay: false
tip.isHovering: false
tip.status: pending
tip.isHovering: true
tip.shouldDisplay: false
tip.isHovering: false
btw, if I remove the Tips.resetDatastore(), it still shows once each time I launch the app.
How would one update the position of a SwiftUI Map without impacting the zoom (or distance from a MapCamera point of view). So want:
a) map position being updated by incoming GPS co-ordinates
b) user may then on the Map zoom in/out
c) on subsequent GPS position changes I want to to keep the zoom/distance changes from the User and not reset these
From the code below the the issue seems to be when getting the current "distance" (i.e. mapCamPost.camera?distance) that this value seems to go to "nil" after the User zooms in the map.
struct GCMap: View {
@StateObject var locationMgr = GcFlightState()
@State private var mapCamPos: MapCameraPosition = .automatic
var body: some View {
ZStack {
Map(position: $mapCamPos) {
Annotation("UserLocation", coordinate: self.locationMgr.location.coordinate) {
Image(systemName: "airplane.circle").rotationEffect(.degrees(270))
}
}
.onMapCameraChange() {
print("onMapCameraChange \(mapCamPos.camera?.distance)")
}
.onReceive(locationMgr.$location) { location in
mapCamPos = .camera(MapCamera(
centerCoordinate: location.coordinate,
distance: mapCamPos.camera?.distance ?? 1000, // <<===
heading: location.course
))
}
}
}
}
I recently encountered a difficult-to-diagnose bug in an app that I'm working on, and I thought I would share it with the Apple Developer community.
The app that I'm working on is an iOS app that uses Core Location's Visit Monitoring API, and it is essential that the app is able to process incoming visits while running in the background. However, after real-world testing, I discovered several crash reports which were difficult to understand, as SwiftUI symbols are not symbolicated on debug builds.
Eventually I discovered that the app was crashing when calling an @EnvironmentObject property of the root ContentView from that view's body when the app was launched directly into the background from not running at all.
After creating a small test app to isolate the problem, I discovered that any environment object declared in the App struct and referenced from the root ContentView causes the crash, with the exception message: "Fatal error: No ObservableObject of type ArbitraryEnvObject found. A View.environmentObject(_:) for ArbitraryEnvObject may be missing as an ancestor of this view."
It seems that when a SwiftUI app is launched in the background, the ContentView's body is executed, but the environment is not initialized. I searched through as much documentation as I could, but could not find any information about how this should be handled, so I think it's a bug in SwiftUI. I have filed a Feedback Assistant bug report.
The current workaround is to convert my ObservableObject into an object that conforms to the new @Observable protocol, add it to the scene as an Environment Value with the .environment(_:) modifier rather than the .environmentObject(_:) modifier, and declare the @Environment property on the view as optional. The object will still be missing when the app is launched in the background, but the optional property can be handled safely to prevent a crash.
Attached to this post is a sample project that demonstrates the issue and the workaround.
And finally, I'd like to hear from anyone who knows more about the expected behavior of background launches of SwiftUI apps, and whether or not there's something I should be doing completely differently.
I'm not able to directly attach a zip archive to this post, so here's an iCloud link to the sample project: BackgroundEnvObjCrash.zip
Hi all,
I am new to Swift and I have found an issue which I can't fix.
I use a TabView and if I change the views a few times, and go back to this view I get this error and I can't tab on any of the views any longer in the Navigation Stack.
The way to fix it, is to force close the app and reopen it.
Error message:
"A NavigationLink is presenting a value of type “NavigationItem” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated.
Note: Links search for destinations in any surrounding NavigationStack, then within the same column of a NavigationSplitView."
Code:
struct ToolsMainView: View {
@State private var navigationPath = NavigationPath()
var body: some View {
NavigationStack(path: $navigationPath) {
VStack {
ScrollView(showsIndicators: false) {
VStack {
ForEach(navigationItems) { item in
NavigationLink(value: item) {
HStack(alignment: .center, spacing: 12) {
Image(systemName: item.icon)
Text(item.title)
Spacer()
Image(systemName: "chevron.right")
}
}
}
}
.scrollTargetLayout()
}
.scrollTargetBehavior(.paging)
}
.navigationDestination(for: NavigationItem.self) { item in
viewMenuItems(item)
}
}
.scrollContentBackground(.hidden)
}
func viewMenuItems(_ tool: NavigationItem) -> some View {
switch tool.menu {
case .forms:
return AnyView(View1())
case .stage:
return AnyView(View2())
case .margin:
return AnyView(View3())
case .vat:
return AnyView(View4())
case .tvheight:
return AnyView(View5))
case .tvsize:
return AnyView(View6())
case .cable:
return AnyView(View7())
case .project:
return AnyView(View8())
case .tasks:
return AnyView(View8())
}
}
}
Do you have any suggestions?
Thank you