I must admit my knowledge of swift is limited, and I cannot wrap my head around this problem.
I've defined this protocol, so I can use different auth providers in my app.
protocol AuthRepository {
associatedtype AuthData
associatedtype AuthResponseData
associatedtype RegistrationData
associatedtype RegistrationResponseData
func login(with data: AuthData) async throws -> AuthResponseData?
func register(with data: RegistrationData) async throws -> RegistrationResponseData?
}
and an implementation for my server
struct MyServerAuthData {
let email: String
let password: String
}
struct MyServerAuthResponseData {
let token: String
}
struct MyServerRegistrationData {
let email: String
let password: String
let name: String
}
actor AuthRepositoryImpl: AuthRepository {
func login(with data: MyServerAuthData) async throws -> MyServerAuthResponseData? {
...
}
func register(with data: MyServerRegistrationData) async throws -> Void? {
...
}
}
To use across the app, I've created this ViewModel
@MainActor
final class AuthViewModel<T: AuthRepository>: ObservableObject {
private let repository: T
init(repository: T) {
self.repository = repository
}
func login(data: T.AuthData) async throws -> T.AuthResponseData? {
try await repository.login(with: data)
}
func register(with data: T.RegistrationData) async throws {
try await repository.register(with: data)
}
}
defined in the app as
@main
struct MyApp: App {
@StateObject var authViewModel = AuthViewModel(repository: AuthRepositoryImpl())
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(self.authViewModel)
}
}
}
and consumed as
@EnvironmentObject private var authViewModel: AuthViewModel<AuthRepositoryImpl>
But with this code, the whole concept of having a generic implementation for the auth repository is useless, because changing the AuthRepostory will need to search and replace AuthViewModel<AuthRepositoryImpl> across all the app.
I've experienced this directly creating a MockAuthImpl to use with #Preview, and the preview crashed because it defines AuthViewModel(repository: MockAuthImpl()) but the view expects AuthViewModel.
There is a better way to do that?
Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi everyone, im in the process of delving more into coregraphics with swiftui, but I am at a roadblock.
First I would like to ask, what are some good resources to learn coregraphics?
Secondly:
I currently have a circle view made and what I want to do is to make my circle view modular so that it can be directly connected to another given circle by a line. How can I do this?
For example, I want my circles to represent nodes and be able to connect by lines to other nodes that are related.
Thanks in advanced.
Here is my code for the circle view:
@State private var circleProgress: CGFloat = 0
let timer = Timer.publish(every: 0.016, on: .main, in: .common).autoconnect()
private let animationDuration: TimeInterval = 1.5
@Binding var startPoint: CGPoint
@Binding var endPoint: CGPoint
var body: some View {
GeometryReader { geometry in
Canvas { context, size in
// Circle parameters
let circleSize: CGFloat = 50
let circleOrigin = CGPoint(
x: size.width / 4,
y: size.height / 2 - circleSize / 2
)
let circleRect = CGRect(
origin: circleOrigin,
size: CGSize(width: circleSize, height: circleSize)
)
let circleCenter = CGPoint(
x: circleOrigin.x + circleSize / 2,
y: circleOrigin.y + circleSize / 2
)
// Animate circle creation
var circlePath = Path()
circlePath.addArc(
center: circleCenter,
radius: circleSize / 2,
startAngle: .degrees(0),
endAngle: .degrees(360 * circleProgress),
clockwise: false
)
context.addFilter(.shadow(color: .white.opacity(0.6), radius: 5, x: 1, y: 1)) // Add white shadow
context.stroke(
circlePath,
with: .linearGradient(
Gradient(colors: [.purple, .white]),
startPoint: circleRect.origin,
endPoint: CGPoint(x: circleRect.maxX, y: circleRect.maxY)
),
lineWidth: 5
)
}
.frame(width: 300, height: 150)
.onReceive(timer) { _ in
// Update circle progress
let progressChange = 0.02 / animationDuration
if circleProgress < 1.0 {
circleProgress = min(circleProgress + progressChange, 1.0)
} else {
circleProgress = 0.0 // Reset the circle to repeat the animation
}
// Get the starting and ending points of the Canvas view
startPoint = CGPoint(x: geometry.frame(in: .global).minX, y: geometry.frame(in: .global).minY)
endPoint = CGPoint(x: geometry.frame(in: .global).maxX, y: geometry.frame(in: .global).maxY)
// Print the points for debugging
print("Start Point: \(startPoint.x), \(startPoint.y)")
print("End Point: \(endPoint.x), \(endPoint.y)")
}
}
.frame(width: 300, height: 150)
}
}
If you add the .scaleEffect() modifier to a parent view inside of which there are children with contextMenu()-s, the context menu preview unfortunately keeps the original, unscaled size of the child view. This results in a very weird, glitchy user experience.
Unfortunately, providing a custom preview that is scaled up also does not help, since even though it is the right size, it gets cropped to the unscaled size of the child view.
Adding scaleEffect() to each child element individually (BEFORE the contextMenu() modifier!) does make the problem disappear. However, I would like to avoid this, since my use case is zooming into a complex graph with context menus on its nodes, and having to recalculate the position of each node manually seems to perform much worse than delegating that work to scaleEffect().
Tested on iOS 18.2 (device + emulator)
Is there a workaround?
Here is a minimal working example that demonstrates the problem:
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
Rectangle()
.frame(width: 100, height: 100)
.contextMenu {
Button("Test") {}
Button("Test") {}
}
Rectangle()
.frame(width: 100, height: 100)
.contextMenu {
Button("Test") {}
Button("Test") {}
}
}
.scaleEffect(1.5)
}
}
Screenshot (problem: The two squares are the same size. However, the long-tapped upper square got shrunk down before the context menu got displayed.)
Topic:
UI Frameworks
SubTopic:
SwiftUI
System: iOS 18.1.1
When connected to Carplay, after playing a song, check the playback page CPNowPlayingTemplate. This error appears on the BMW car, as shown in the picture:
In our project, this is achieved using the following methods:
UIImage *image1 = [UIImage imageNamed:@"imageName"];;
CPNowPlayingImageButton *button1 = [[CPNowPlayingImageButton alloc] initWithImage:image1 handler:^(__kindof CPNowPlayingButton * _Nonnull action) {
//do something
}];
UIImage *image2 = [UIImage imageNamed:@"imageName"];;
CPNowPlayingImageButton *button2 = [[CPNowPlayingImageButton alloc] initWithImage:image2 handler:^(__kindof CPNowPlayingButton * _Nonnull action) {
//do something
}];
NSArray<CPNowPlayingButton *> *buttons;
buttons = @[button1,button2];
[[CPNowPlayingTemplate sharedTemplate] updateNowPlayingButtons:buttons];
Is there any way to solve this problem?
Its important to note that this same app did not have this issue in iOS 17.
Ever since iOS 18 I have noticed that when application written in SwiftUI uses Label with the default color (which auto changes between light and dark appearance), from time to time when resuming an application that has been in the background, the color of those labels (only the Label elements) switches from the opposite to the correct one. Here is an example:
Steps to reproduce
Phone is in dark appearance
Open app
Labels text color is white and labels background is black
Go to home so that app is on background
Wait random time (does not happen all the time), some times 1 min some times 10
Reopen the application.
During the opening transition the Label text color was changed while the app was in suspended mode to the light appearance variant (black)
Once the app opening transition finishes the Label text color switches back to the correct color for dark appearance (white)
Same issue happens if you start from light appearance. I cannot reproduce this on Xcode simulators, I have tried to force memory warning to check if that has anything to do with it but that also does not cause the issue to appear on simulators. For now I can only reproduce this on real device.
Screenshots
Here is screenshots of the above example:
During transition
After transition
Topic:
UI Frameworks
SubTopic:
SwiftUI
Let's say I want to build a simple photo management app on Mac or iPad with Swift UI. This app has multi-window support. My photos are organized inside albums. I should be able to drag photos between windows from one album to another.
I struggle to get this working properly with Swift UI. Writing modern code I would like to use Transferable. Let's say my photos are not real files. So I can't use a FileRepresentation. Instead I use CodableRepresentation and encode an identifier. This identifier is later used to drive the move operation between folders.
I ran into some limitations here
Transferable seems to be meant for copy-like Drag & Drop operations. I have no possible to get a "move" cursor on macOS (it's always the copy cursor with the green + sign). Also the API reads like it is about importing/exporting – not moving.
When using dropDestination on ForEach, I completely lack the possibility to deny a drop (e.g. when a photo is already part of the album).
I'd like to have modifier key to switch between copying and moving.
Sometimes a drop should be redirected to a different index. How to do that?
Is there any chance to do this with Transferable? It even doesn't seem to be easy with NSItemProvider and onDrop/onDrag? Or should we still use a plain old UICollectionView/NSCollectionView, if we want to have more sophisticated control over drag/drop validation?
Hi.
I know to know which window gets hardware keyboard events (such as shortcut key) currently on iPad.
Until iPadOS 15.0, UIApplication.shared.keyWindow, which was deprecated on iPadOS 13.0 and didBecomeKeyNotification/didResignKeyNotification.
But after iPadOS 15.0, a keyWindow is managed by UIScene, not by UIApplication.
Each scene of my app always has just one window.
For my purpose, checking deprecated UIApplication.shared.keyWindow is still effective but didBecomeKeyNotification and didResignKeyNotification don't work because they are fired when a change happens only inside the scene.
So my questions are,
What is the new alternative of UIApplication.shared.keyWindow?
I know a wrong hack like
UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first?.windows.filter { $0.isKeyWindow }.first
does not work since the order of connectedScenes is not related with getting hardware keyboard events.
What are the new alternatives of didBecomeKeyNotification/didResignKeyNotification which work on inter-scene?
The second question is more crucial.
Because about the first question, I can still use deprecated UIApplication.shared.keyWindow.
Thanks.
I have a swiftui view with Button(intent: ) and using UIHostingViewcontroller to use it in UIKit. The problem is that button not works in uikit but normal button(without intent works)
macOS: 15.0
macFUSE: 4.8.3
I am using rclone + macFUSE and mount my netdisk where it has created three subdirectories in its root directory: /user, /share, and /group.
When I save a file to /[root]/user using NSSavePanel and name it test.txt, I expect the file to be saved as:
/[root]/user/test.txt
However, occasionally, the delegate method:
- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError {
}
returns an incorrect path:
/[root]/test.txt
This issue only occurs when selecting /user. The same operation works correctly for /share and /group.
Is there any logs I could provide to help solving this issue?
Many thanks!
When I try to launch a ShareLink from within a fullScreenCover, I get the following error:
Attempt to present <UIActivityViewController: 0x105e6a400> on <_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x1053a37c0> (from <_TtGC7SwiftUI19UIHostingControllerVVS_7TabItem8RootView_: 0x105eb8a00>) which is already presenting <_TtGC7SwiftUI29PresentationHostingControllerVS_7AnyView_: 0x114890000>
Seems like a bug, has anyone else encountered this or found a way around it?
Hi everyone,
I believe this should be a simple and expected default behavior in a real-world app, but I’m unable to make it work:
1. I have a View (a screen/page in this case) that calls an endpoint using async/await.
2. If the endpoint hasn’t finished, but I navigate forward to a DetailView, I want the endpoint to continue fetching data (i.e., inside the @StateObject ViewModel that the View owns). This way, when I go back, the View will have refreshed with the fetched data once it completes.
3. If the endpoint hasn’t finished and I navigate back to the previous screen, I want it to be canceled, and the @StateObject ViewModel should be deinitialized.
I can achieve 1 and 3 using the .task modifier, since it automatically cancels the asynchronous task when the view disappears:
view
.task { await vm.getData() }
I can achieve 1 and 2 using a structured Task in the View (or in the ViewModel, its the same behavior), for example:
.onFirstAppearOnly {
Task { away vm.getData() }
}
onFirstAppearOnly is a custom modifier that I have for calling onAppear only once in view lifecycle. Just to clarify, dont think that part is important for the purpose of the example
But the question is: How can I achieve all three behaviors? Is this really such an unusual requirement?
My minimum deployment target is iOS 15, and I’m using NavigationView + NavigationLink. However, I have also tried using NavigationStack + NavigationPath and still couldn’t get it to work.
Any help would be much appreciated.
Thank you, folks!
I have a NavigationSplitView with all three columns, and NavigationLinks in the sidebar to present different Lists for the content column. When a list item is selected in the content view, I want to present a view in the detail view. Since different detail views should be presented for different content views, I represent state like this (some details omitted for space):
// Which detail view to show in the third column
enum DetailViewKind: Equatable {
case blank
case localEnv(RegistryEntry)
case package(SearchResult)
}
// Which link in the sidebar was tapped
enum SidebarMenuItem: Int, Hashable, CaseIterable {
case home
case localEnvs
case remoteEnvs
case packageSearch
}
// Binds to a DetailViewKind, defines the activate SidebarMenuItem
struct SidebarView: View {
@State private var selectedMenuItem: SidebarMenuItem?
@Binding var selectedDetailView: DetailViewKind
var body: some View {
VStack(alignment: .leading, spacing: 32) {
SidebarHeaderView()
Divider()
// Creates the navigationLinks with a SidebarMenuRowView as labels
SidebarMenuView(selectedMenuItem: $selectedMenuItem, selectedDetailView: $selectedDetailView)
Spacer()
Divider()
SidebarFooterView()
}
.padding()
.frame(alignment: .leading)
}
}
struct SidebarMenuRowView: View {
var menuItem: SidebarMenuItem
@Binding var selectedMenuItem: SidebarMenuItem?
@Binding var selectedDetailView: DetailViewKind
private var isSelected: Bool {
return menuItem == selectedMenuItem
}
var body: some View {
HStack {
Image(systemName: menuItem.systemImageName).imageScale(.small)
Text(menuItem.title)
Spacer()
}
.padding(.leading)
.frame(height: 24)
.foregroundStyle(isSelected ? Color.primaryAccent : Color.primary)
.background(isSelected ? Color.menuSelection : Color.clear)
.clipShape(RoundedRectangle(cornerRadius: 10))
.navigationDestination(for: SidebarMenuItem.self) { item in
navigationDestinationFor(menuItem: item, detailView: $selectedDetailView)
}
.onTapGesture {
if menuItem != selectedMenuItem {
selectedMenuItem = menuItem
}
}
}
}
// Determines which detail view to present
struct DetailView: View {
@Binding var selectedDetailView: DetailViewKind
var innerView: some View {
switch selectedDetailView {
case .blank:
AnyView(Text("Make a selection")
.font(.subheadline)
.foregroundStyle(.secondary)
.navigationSplitViewColumnWidth(min: 200, ideal: 350))
case .localEnv(let regEntry):
AnyView(EnvironmentDetailView(regEntry: regEntry))
case .package(let searchResult):
AnyView(PackageDetailView(searchResult: searchResult))
}
}
var body: some View {
innerView
}
}
struct ContentView: View {
@State private var detailView: DetailViewKind = .blank
var body: some View {
NavigationSplitView {
SidebarView(selectedDetailView: $detailView)
.navigationSplitViewColumnWidth(175)
} content: {
HomeView()
.navigationSplitViewColumnWidth(min: 300, ideal: 450)
}
detail: {
DetailView(selectedDetailView: $detailView)
}
}
}
My issue is that the detail view is not updated when the ContentView's detailView property is updated. I've verified that the value itself is changing, but the view is not. I searched around to see why this would be (this is my first Swift/SwiftUI application) and from what I gather a @Binding alone will not cause a view to be updated, that binding either needs to be used in the view hierarchy, or it needs to be stored as a @State property that gets updated when the binding value changes. I added a dummy @State property to DetailView and that still doesn't work, so I'm a little confused:
struct DetailView: View {
@Binding var selectedDetailView: DetailViewKind
@State private var dummyProp: DetailViewKind?
var innerView: some View {
switch selectedDetailView {
case .blank:
AnyView(Text("Make a selection")
.font(.subheadline)
.foregroundStyle(.secondary)
.navigationSplitViewColumnWidth(min: 200, ideal: 350))
case .localEnv(let regEntry):
AnyView(EnvironmentDetailView(regEntry: regEntry))
case .package(let searchResult):
AnyView(PackageDetailView(searchResult: searchResult))
}
}
var body: some View {
innerView.onChange(of: selectedDetailView) {
dummyProp = selectedDetailView
}
}
}
Any ideas?
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hello!
I'm trying to set a UiRefreshControl.tintColor:
.onAppear {
UIRefreshControl.appearance().tintColor = UIColor.systemBlue
}
But instead of
I get
The color in the second picture is a high contrast version of the first one. I can't understand why it works this way.
I also tried the following.
UIRefreshControl.appearance().tintColor = UIColor(red: 0, green: 0.478, blue: 1, alpha: 1) // doesn't work
UIRefreshControl.appearance().tintColor = UIColor(named: "RefreshControlColor") // doesn't work, here set "High contrast" on and indicated Universal.systemBlueColor
Perhaps I missed something?
Hi guys, I’m making a simple note taking app and I want to support markdown functionality. I have tried to find libraries and many other GitHub repos but some of them are slow and some of them are very hard to implement and not very customizable.
In WWDC 22 apple also made a markdown to html document app and I also looked at that code and it was awesome. It was fast and reliable (Apple btw).
But the only problem I am facing is that the markdown text is on the left side and the output format is on the right in the form of html. I don’t want that I want both in the same line. In bear notes and things 3 you can write in markdown and you can see that it is converting in the same line. I have also attached example videos. So, I have markdown parser by apple but the only thing in the way is that it is converting it into a html document. Please help me with this. Also please look into the things 3 video they have also completely customized the text attributes selection menu. By default with UITextView we can only enable text attributes and it shows like this.
By clicking more we get the complete formatting menu but not the slider menu which is more convenient. Please also help me this. I don’t know if I can provide apple file but it is from wwdc 22 supporting desktop class interaction
Hi there, I'm currently using UIHostingController to display swift charts in uikit. The problem im facing is that the UIHostingController isn't outputting the intended theme. When the simulator/phone is on dark mode the view is still in light mode. Iv'e tried to force the view to use dark mode with:
.environment(\.colorScheme, .dark)
But it doesn't seem to help. Here's how I implement the UIHostingController to my view:
let controller = UIHostingController(rootView: StatVC())
controller.view.translatesAutoresizingMaksIntoConstraints = false
addChild(controller)
controller.didMove(toParent: self)
view.addSubview(controller.view)
where StatVC() is the swiftui view which contains the swift chart.
I am implementing a new Intents UI Extension and am noticing that the viewWillDisappear, viewDidDisappear, and deinit methods are not being called on my UIViewController that implements INUIHostedViewControlling, when pressing the "Done" button and dismissing the UIViewController.
This causes the memory for the UI Extension to slowly increase each time I re-run the UI Extension until it reaches the 120MB limit and crashes.
Any ideas as to what's going on here and how to solve this issue?
Here is the reproducible codes:
struct JumpView: View {
var body: some View {
NavigationStack {
TabView {
Text("Jump")
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button("Done") {}
}
}
}
}
}
}
Run directly in real apple watch device in watchOS 10.0+ (do not debug connecting with Xcode).
When raise your wrist the ToolBar Button will jump weirdly.
in ios it is not same as it in computer
there is text:"ยินดี
in computer is
but in ios it is
the fontsize is 16
I tried every font which is in ios and tried copy simsun in windows to ios and create CTFont
I draw it using UIGraphics drawString
I am developing a watchOS app with a tab view, and inside one of the tabs, I have a 2x2 collection view layout. Each collection item contains a view that handles:
Tap Gesture (.onTapGesture) → Executes an action immediately.
Long Press Gesture (LongPressGesture) → Triggers an update when long press starts and again when it ends.
Drag Gesture (DragGesture) → Updates the position while dragging and resets values when the gesture ends.
Issue:
Most of the time, the gestures work correctly, but if another finger swipes on the screen while a long press is active, sometimes the onEnded event does not trigger, leaving the button in an active state.
Observations:
The onTapGesture executes normally without issues.
The LongPressGesture starts as expected, but if another finger interacts with the screen, the onEnded of the drag gesture does not always trigger.
This results in the button remaining visually or functionally "pressed" until another interaction occurs.
Has anyone encountered similar behavior in watchOS? How can I ensure that long press and drag gestures always complete even when another finger swipes on the screen?
Thanks in advance!
We are currently trying to adopt the newly introduced find bar in our app.
The app:
The app is a text editor with a main text view. However it includes nested views (for text like footnotes) that are presented as modal sheets. So you tap on the footnote within the main text, a form sheet is presented with the contents of the footnote ready to be edited. We have an existing search implementation, but are eager to move to the system-provided UI. Connecting the find bar through a custom UIFindSession with our existing implementation is working without any issues.
The Problem:
Searching for text does not only work in the main text view, but also nested text (like footnotes). Let's say I have a text containing the word "iPhone" both in the main text and the footnote. In our existing implementation, stepping from the search match to the next one would open the modal and highlight the match in the nested text. The keyboard would stay open.
With the new UIFindInteraction this is not working however. As soon as a modal form sheet is presented, the find interaction closes. By looking at the stack trace I can see a private class called UIInputWindowController that cleans up input accessory views after the modal gets presented. I believe it is causing the find panel to give up its first responder state. I noticed that opening popovers appears to be working fine.
Is there a way to alter the presentation of the nested text so that the view is either not modal or able to preserve the current find session? Or is this unsupported behavior and we should try and look for a different way?
The thing that really confuses me is that this appears to work without issue in Notes.app. There the find bar is implemented as well. There are multiple views that can be presented while the find bar is open. Move Note is one of them. The view appears as a modal sheet. It keeps the find bar open and active, though its tint color matches the deactivated one of the main Notes view. The find bar is still functional with the text field being active and the overlay updating in the background. This behavior appears to be a bug in the Notes app, but is exactly what we want for our use case.
I attached some images: Two are from the Notes app, two from a test project demonstrating the problem. Opening a modal view closes the find bar there.