Hello everyone!
I have a WKWebView in my swiftui app and would like to enable to "pop root view when selected tab is tapped again" feature, but I have been unable to figure out how to implement this.
Here's the basic code:
class TabIdentifierModel:ObservableObject {
@Published var tabSelection:TabIdentifier {
willSet {
if newValue == tabSelection {
NotificationCenter.default.post(name: .popRootView, object: nil, userInfo: ["tab": newValue.rawValue])
}
}
}
init() {
tabSelection = .home
}
}
struct ContentView: View {
@AppStorage(AppStorageKeys.enableShorts) var enableShorts = true
@StateObject var storeVM = StoreVM()
@StateObject var downloadURLManager = DownloadURLManager.shared
@State var downloadViewIsOpen = false
// ......
@State var tabSelection = TabIdentifierModel()
var body: some View {
TabView(selection: $tabSelection.tabSelection) {
// ......
WebViewWrapper(url: $libraryTabURL)
.tabItem {
Label {
Text("Library")
} icon: {
Image(systemName: "folder.fill")
}
}.tag(TabIdentifier.library)
// ......
}
.environmentObject(tabSelection)
}
}
Tapping on the tab again doesn't seem to set the value again thus the NotificationCenter.default.post is not sent and the web view is not reloaded.
Help would be much appreciated! Thanks in advance!
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
In my case, I open an immersiveSpace and windowGroupA together. When I successfully push a new windowGroupB from windowGroupA, dismissing windowGroupB should return me to windowGroupA. However, this fails, and windowGroupA remains in the background scenePhase. I tried closing immersiveSpace, and it worked successfully. Therefore, pushWindow cannot be used when immersiveSpace is open.
Greetings -
If I build an app using DocumentGroup, I get the document picker when the app launches.
A good iOS app should restore the state the app was in when it was closed. It seems like with DocumentGroup, when the user leaves my app and the app is killed, the next time they launch it, they have to remember the document they were working on and reopen it.
How can I have my DocumentGroup-based app restore the last open document at launch?
FB13874563
Thanks - Steve
Hello everyone,
I'm working on a SwiftUI app that requires location services, and I've implemented a LocationManager class to handle location updates and permissions. However, I'm facing an issue where the location permission popup does not appear when the app is launched.
Here is my current implementation:
LocationManager.swift:
import CoreLocation
import SwiftUI
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
@Published var userLocation: CLLocation?
@Published var isAuthorized = false
@Published var authorizationStatus: CLAuthorizationStatus = .notDetermined
override init() {
super.init()
locationManager.delegate = self
checkAuthorizationStatus()
}
func startLocationUpdates() {
locationManager.startUpdatingLocation()
}
func stopLocationUpdates() {
locationManager.stopUpdatingLocation()
}
func requestLocationAuthorization() {
print("Requesting location authorization")
DispatchQueue.main.async {
self.locationManager.requestWhenInUseAuthorization()
}
}
private func checkAuthorizationStatus() {
print("Checking authorization status")
authorizationStatus = locationManager.authorizationStatus
print("Initial authorization status: \(authorizationStatus.rawValue)")
handleAuthorizationStatus(authorizationStatus)
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print("Authorization status changed")
authorizationStatus = manager.authorizationStatus
print("New authorization status: \(authorizationStatus.rawValue)")
handleAuthorizationStatus(authorizationStatus)
}
private func handleAuthorizationStatus(_ status: CLAuthorizationStatus) {
switch status {
case .authorizedAlways, .authorizedWhenInUse:
DispatchQueue.main.async {
self.isAuthorized = true
self.startLocationUpdates()
}
case .notDetermined:
requestLocationAuthorization()
case .denied, .restricted:
DispatchQueue.main.async {
self.isAuthorized = false
self.stopLocationUpdates()
print("Location access denied or restricted")
}
@unknown default:
DispatchQueue.main.async {
self.isAuthorized = false
self.stopLocationUpdates()
}
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
DispatchQueue.main.async {
self.userLocation = locations.last
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location manager error: \(error.localizedDescription)")
}
}
MapzinApp.swift:
@main
struct MapzinApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
@StateObject private var locationManager = LocationManager()
var body: some Scene {
WindowGroup {
Group {
if locationManager.authorizationStatus == .notDetermined {
Text("Determining location authorization status...")
} else if locationManager.isAuthorized {
CoordinatorView()
.environmentObject(locationManager)
} else {
Text("Location access is required to use this app. Please enable it in Settings.")
}
}
}
}
}
Log input:
Checking authorization status
Initial authorization status: 0
Requesting location authorization
Authorization status changed
New authorization status: 0
Requesting location authorization
Despite calling requestWhenInUseAuthorization() when the authorization status is .notDetermined, the permission popup never appears. Here are the specific steps I have taken:
Checked the Info.plist to ensure the necessary keys for location usage are present:
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
NSLocationAlwaysAndWhenInUseUsageDescription
Verified that the app's target settings include location services capabilities.
Tested on a real device to ensure it's not a simulator issue.
I'm not sure what I might be missing. Any advice or suggestions to resolve this issue would be greatly appreciated. Thank you!
When user triggers a sheet of a view, it's quite easy to deal with its confirmation action. However, how can I deal with the cancellation action, which means when user taps the cancel button, what they edits can re-become its original version ( I am using SwiftData)?
Some data have skipped dates, as in the following data.
TrainingSession(date: formatter.date(from: "2024-05-12 07:37:30 +0000")!, maxRM: 10.0, totalVolume: 0.0),
TrainingSession(date: formatter.date(from: "2024-06-01 15:00:00 +0000")!, maxRM: 10.5, totalVolume: 105.0),
TrainingSession(date: formatter.date(from: "2024-06-03 15:00:00 +0000")!, maxRM: 10.0, totalVolume: 100.0)
In this case, the graph shows nothing for the corresponding date as shown in the image.
s it possible to create a continuous graph by displaying only the data with values and not the dates with no values?
The source code is as follows
// ContentView.swift
// GraphSample
//
// Created by 齋藤卓馬 on 2024/06/09.
//
import SwiftUI
import Charts
struct TrainingSession {
var date: Date
var maxRM: Double
var totalVolume: Double
}
struct GraphView: View {
var sessions: [TrainingSession]
var body: some View {
ScrollView {
VStack(alignment: .leading) {
// 最大RMのグラフ
VStack(alignment: .leading) {
Text("最大RM")
.font(.headline)
.padding()
Chart(sessions, id: \.date) { session in
BarMark(
x: .value("Date", session.date),
y: .value("Max RM", session.maxRM)
)
}
.chartXAxis {
AxisMarks(values: .stride(by: .day, count:7)) // 日付の表示間隔を調整
}
.chartScrollableAxes(.horizontal) // 横スクロールを有効にする
.padding([.leading, .trailing, .bottom])
}
// 総負荷量のグラフ
VStack(alignment: .leading) {
Text("総負荷量")
.font(.headline)
.padding()
Chart(sessions, id: \.date) { session in
BarMark(
x: .value("Date", session.date),
y: .value("Total Volume", session.totalVolume)
)
}
.chartXAxis {
AxisMarks(values: .stride(by: .day, count:7)) // 日付の表示間隔を調整
}
.chartScrollableAxes(.horizontal) // 横スクロールを有効にする
.padding([.leading, .trailing, .bottom])
}
}
}
}
}
struct ContentView: View {
var body: some View {
GraphView(sessions: sampleData)
}
var sampleData: [TrainingSession] {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
return [
TrainingSession(date: formatter.date(from: "2024-05-12 07:37:30 +0000")!, maxRM: 10.0, totalVolume: 0.0),
TrainingSession(date: formatter.date(from: "2024-06-01 15:00:00 +0000")!, maxRM: 10.5, totalVolume: 105.0),
TrainingSession(date: formatter.date(from: "2024-06-03 15:00:00 +0000")!, maxRM: 10.0, totalVolume: 100.0)
]
}
}
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
I’d like to create a simple Gantt chart where each horizontal BarMark is a navigation link to a detail view.
When I embed a navigation link within a chart, I get the error
“Static method 'buildExpression' requires that 'some ChartContent' conform to 'View’”
NavigationLink(value: taskGroup) {
BarMark(
xStart: .value("Start", taskGroup.start),
xEnd: .value("End", taskGroup.end),
y: .value("Event", taskGroup.taskGroupName),
height: barHeight
)
}
I could use a chart overlay and manage the navigation from there, but it appears I can only grab published chart data at a given tap gesture. I need the object itself to inject into the detail view (in this case TaskGroup) and the data I’m plotting in the chart isn’t unique - so no obvious way to identify which TaskGroup the user tapped.
Due to the varying number of days in each month, I am unsure how to enable monthly paging in Charts.
In Apple's official example, SwiftChartsExample, there is only an example showing the sales of the "last 30 days":
.chartXVisibleDomain(length: 3600 * 24 * 30)
I have tried using scrollPosition to calculate the number of days in the current month, like this:
var days: Int {
let current = Calendar.current
let dateRange = current.range(of: .day, in: .month, for: scrollPosition)
return dateRange?.count ?? 0
}
...
.chartXVisibleDomain(length: 3600 * 24 * days)
...
.chartScrollPosition(x: $scrollPosition)
...
But I found that it does not work as expected. 😢
Hello all,
if I enable the .chartScrollableAxes(.horizontal) and .chartXVisibleDomain(length: length) for a chart view to zoom in the screenshot of the view misses the graphs.
I use this extension:
`extension View {
@MainActor func snapshot() {
let renderer = ImageRenderer(content: self)
if let exportImage = renderer.nsImage {
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.writeObjects([exportImage])
}
}
}`
The screenshot is taken with:
Button("Snap") {
let view = ChartView(text: $statusText, length: $chartLength)
.padding()
.frame(width: 1500, height: 500)
view.snapshot()
}
If I omit .chartScrollableAxes(.horizontal) the snapshot is ok and the graphs are visible in the image but then a zoom is not possible and the whole range is shown.
Any ideas?
When will SwiftUI plot will support dates on the x-axes (hrs, month, years) and when will the plot library supports logarithmic scales.
Great progress so far.
Thanks.
I'm trying to add QuickLook previews to a SwiftUI app which uses Table().
I've added the .quickLookPreview() modifier to my Table(), and added a menu command for invoking it, and if I select that menu item manually, it works fine, but I have two keyboard related issues which are making it difficult to actually ship this functionality:
When using the .quickLookPreview() variant for a set of URLs, keyboard navigation between the quicklook previews only works with left/right arrows, but being invoked by a Table, it would make much more sense for up/down arrows to navigate through the previews
I set a .keyboardShortcut() on the menu command to use Space, since that's the normally-expected shortcut for quicklook, and it doesn't work. If I set it to some random other key (like "a") it does work, but .space doesn't do anything.
Is there a system deep link URI to the built in files app? I would like to direct users to my apps location in the files app. For example files://myApp
The only exposed deep links for system I can find are the ones for mail, sms, FaceTime etc.
Thank you
(tag used for post was because I couldn’t find a deep link tag)
App is Xcode swiftUI on iPad using textview. The cursor is invisible yet it moves "underneath". How to make it visible. The app textview input is button characters and coexists with a Magic Keyboard where the cursor is visible.
I’m migrating my UIPopoverPresentationController to SwiftUI’s .popover modifier.
In doing so, I hit a stumbling block. Previously, I had used passthroughViews to avoid the popover closing on certain taps.
How could I replicate this behavior with SwiftUI?
For some reason, every time I'm accessing a view populated with posts and fetching posts, my app has like a 5-8 second hang time. I don't know whats causing this issue, and I have been dealing with this for 3 days.
`func fetchPosts(completion: @escaping (Result<[PostWithUser], Error>) -> Void) {
if !cachedPosts.isEmpty {
completion(.success(self.cachedPosts))
return
}
postsRef.order(by: "timestamp", descending: true).getDocuments { [weak self] snapshot, error in
guard let self = self else { return }
if let error = error {
completion(.failure(error))
return
}
guard let documents = snapshot?.documents else {
completion(.success([]))
return
}
let group = DispatchGroup()
var postsWithUsers: [PostWithUser] = []
DispatchQueue.global(qos: .userInitiated).async {
for document in documents {
group.enter()
let data = document.data()
guard let userId = data["userId"] as? String,
let parentId = data["parentId"] as? String,
let groupId = data["groupId"] as? String,
let text = data["text"] as? String,
let isPinned = data["isPinned"] as? Bool,
let imageUrl = data["imageUrl"] as? String,
let videoUrl = data["videoUrl"] as? String,
let timestamp = data["timestamp"] as? Timestamp,
let views = data["views"] as? Int else {
group.leave()
continue
}
let post = Post(id: document.documentID, userId: userId, parentId: parentId, groupId: groupId, text: text, imageUrl: imageUrl, videoUrl: videoUrl, timestamp: timestamp.dateValue(), isPinned: isPinned, likedBy: [], views: views)
self.usersRef.document(userId).getDocument { userDocument, error in
defer { group.leave() }
if let userDocument = userDocument, let userData = userDocument.data() {
let user = User(
id: userId,
username: userData["username"] as? String ?? "",
bio: userData["bio"] as? String ?? "",
profilePictureUrl: userData["profileImageUrl"] as? String ?? "",
privateProfile: userData["privateProfile"] as? Bool ?? false,
privateFollowerList: userData["privateFollowerList"] as? Bool ?? false,
privateFollowingList: userData["privateFollowingList"] as? Bool ?? false,
privateReplies: userData["privateReplies"] as? Bool ?? false,
privateLikes: userData["privateLikes"] as? Bool ?? false
)
let postWithUser = PostWithUser(post: post, user: user)
DispatchQueue.main.async {
postsWithUsers.append(postWithUser)
}
} else {
print("Failed to fetch user data for userId: \(userId), error: \(String(describing: error))")
}
}
}
group.notify(queue: .main) {
self.cachedPosts = postsWithUsers
completion(.success(postsWithUsers))
}
}
}
}`
`
func fetchPosts() {
PostService.shared.fetchPosts { result in
switch result {
case .success(let posts):
DispatchQueue.main.async {
self.posts = posts
}
case .failure(let error):
print("Failed to fetch posts: \(error)")
}
}
}
func refreshPosts() {
PostService.shared.refreshPosts { result in
switch result {
case .success(let posts):
DispatchQueue.main.async {
self.posts = posts
}
case .failure(let error):
print("Failed to fetch posts: \(error)")
}
}
}
Hello !
This will be my first blog post. I hope I will find the solution.
So, I try to use the new iOS 17+ API for ScrollView. I have an array of messages that I want to show in scroll view.
And I need to scroll to bottom when I tap to Send button. When I use scrollPosition(id:anchor:), it freeze the UI. The link to the screencast to demonstrate. I think the problem might be the keyboard when it shows and hides. But I can't understand what can I do.
struct ChatView: View {
@ObservedObject var viewModel: CoachViewModel
@State private var scrollTo: Message.ID?
var body: some View {
ScrollView {
LazyVStack {
ForEach(viewModel.messages) { message in
MessageView(viewModel: viewModel, message: message)
}
}
.scrollTargetLayout()
}
.scrollPosition(id: $scrollTo)
.background(.primaryBackground)
.onChange(of: viewModel.scrollToBottom) {
if $1, let lastMessage = viewModel.messages.last {
withAnimation {
scrollTo = lastMessage.id
}
viewModel.scrollToBottom = false
}
}
}
}
struct Message: Identifiable, Hashable {
let id: UUID
let author: MessageAuthor
let text: String
}
final class CoachViewModel: ObservableObject {
@Published var messageTextInput = ""
@Published var scrollToBottom = false
@Published var messages: [Message] = [...]
// ... more code ...
func sendMessage() {
messages.append(
.init(
id: .init(),
author: .user,
text: messageTextInput
)
)
messageTextInput.removeAll()
scrollToBottom = true
}
}
struct CoachView: View {
@StateObject private var viewModel = CoachViewModel()
@State private var isSearching = false
var body: some View {
VStack(spacing: 0) {
Group {
CoachTitleView(viewModel: viewModel, isSearching: $isSearching)
ChatView(viewModel: viewModel)
}
.onTapGesture {
UIApplication.shared.endEditing()
}
if isSearching {
MatchboxView(viewModel: viewModel)
} else {
InputTextFieldView(viewModel: viewModel)
}
}
}
}
0
I'm trying to make a list of trips that a person has gone on, and when someone has no trips in their list, it will display a ContentUnavailableView with a NavigationLink to take them to a new view. I am encountering strange issues when using the ContentUnavailableView with the NavigationLink, such as the back button being unaligned and not being able to swipe back to the previous view.
I expected the ContentUnavailableView to link without any of these issues.
Any guidance would be greatly appreciated.
I want to display my own image in an inline widget. Using the SwiftUI Image syntax doesn't show the image, so following advice from online forums, I used the syntax Image(uiImage: UIImage(named: String)). This successfully displays the image, but if I change the image file name in the app, the image doesn't update properly.
I tested displaying the image file name using Text in the inline widget, and it correctly shows the updated file name from my app. So, my AppStorage and AppGroups seem to be working correctly.
I'd like to ask if there's a way to update my images properly and if there's an alternative method to display images without converting them to UIImage. Thanks.
Hi, thought I'd share some feedback (which was also sent to Apple) publicly in case anyone else is experiencing the same issue or has an idea of how to circumvent the issue while Apple investigates.
TLDR: Views presented with the .fullScreenCover modifier can be incorrectly dismissed when a child view presented with a .sheet modifier inside the .fullScreenCover is dismissed.
The intended behavior is that the child sheet views can be dismissed without dismissing the parent full screen cover views. It appears this bug has been present in versions of iOS 17 as well as iOS 18.
Short of using something other than a .fullScreenCover to present the view, I'm still searching for a solution to this bug.
Filing: FB14007758
Steps to Reproduce (see code below):
Open a View in .fullScreenCover presentation
In the view presented via the .fullScreenCover, add two sheet modifiers with two different views (e.g. SheetOneView, SheetTwoView)
Toggle the presentation of SheetOneView and SheetTwoView randomly until the .fullScreenCover is incorrectly dismissed.
Reproducible Code:
import SwiftUI
struct SheetOneView: View {
var body: some View {
Text("Sheet: 1")
}
}
struct SheetTwoView: View {
var body: some View {
Text("Sheet: 2")
}
}
struct FullScreenCoverView: View {
@State var isSheetOnePresented = false
@State var isSheetTwoPresented = false
var body: some View {
VStack(content: {
Text("Full Screen Cover")
HStack {
Button("Open Sheet 1") {
isSheetOnePresented = true
}
Button("Open Sheet 2") {
isSheetTwoPresented = true
}
}
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
})
.sheet(isPresented: $isSheetOnePresented) {
SheetOneView()
}
.sheet(isPresented: $isSheetTwoPresented) {
SheetTwoView()
}
}
}
struct ContentView: View {
@State var isFullScreenCoverPresented = false
var body: some View {
VStack {
Button("Open Full Screen Cover") {
isFullScreenCoverPresented = true
}
}
.fullScreenCover(isPresented: $isFullScreenCoverPresented, content: {
FullScreenCoverView()
})
.padding()
}
}
I want to display my own image in an inline widget. Using the SwiftUI Image syntax doesn't show the image, so following advice from online forums, I used the syntaxImage(uiImage: UIImage(named: String))This
successfully displays the image, but if I change the image file name in the app, the image doesn't update properly.
I tested displaying the image file name using Text in the inline widget, and it correctly shows the updated file name from my app. So, my AppStorage and AppGroups seem to be working correctly.
I'd like to ask if there's a way to update my images properly and if there's an alternative method to display images without converting them to UIImage. Thanks.