Construct and manage graphical, event-driven user interfaces for iOS or tvOS apps using UIKit.

Posts under UIKit tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Simulating key press event to type text in UITextField
in iOS, user can set focus on UItextField and tapping a key in the virtual keyboard updates the text in the textfield. This user action causes the relevant delegates of UITextFieldDelegate to get invoked, i.e the handlers associated with action of user entering some text in the textfield. I m trying to simulate this user action where I am trying to do this programatically. I want to simulate it in a way such that all the handlers/listeners which otherwise would have been invoked as a result of user typing in the textfield should also get invoked now when i am trying to do it programatically. I have a specific usecase of this in my application. Below is how I m performing this simulation. I m manually updating the text field associated(UITextField.text) and updating its value. And then I m invoking the delegate manually as textField.delegate?.textField?(textField, shouldChangeCharactersIn: nsRange, replacementString: replacementString) I wanted to know If this is the right way to do this. Is there something better available that can be used, such that simulation has the same affect as the user performing the update?
0
0
189
2w
Is using to SwiftUI entry point and using NSViewControllerRepresentable to add NSViewController to hirarchy right approach ?
In macOS application, we are using SwiftUI as an entry point to our application and attaching appdelegate using NSApplicationDelegateAdaptor. We are using NSViewControllerRepresentable to add a View Controller to the hiracrchy so that we can store intance of viewcontroller and add content to it programatically . @main struct TWMainApp: App { @NSApplicationDelegateAdaptor private var appDelegate: TWAppDelegate internal var body : some Scene { TWInitialScene () } } TWInitialScene : public struct TWInitialScene : Scene { public var body : some Scene { WindowGroup { TWInitialView () } } } TWInitialView : struct TWInitialView : View { @Environment(\.scenePhase) private var scenePhase var body : some View { TWAppKitToSwiftUIBridge () } } TWAppKitToSwiftUIBridge : struct TWNSKitToSwiftUIBridge : NSViewControllerRepresentable { func makeNSViewController(context: Context) -> TWNSViewController { let view_hierarchy : TWNSViewController view_hierarchy = TWStaticContext.sViewController return view_hierarchy } func updateNSViewController(_ nsViewController: TWNSViewController, context: Context) { } } @objc public class TWStaticContext : NSObject { public static let sViewController = TWNSViewController () public override init () {} @objc public static func GetViewController () -> TWNSViewController { return TWStaticContext.sViewController } } public class TWNSViewController : NSViewController { override public func viewDidLoad () { super.viewDidLoad () } } To add content to the hirarchy we are accessing viewcontroller's intance and adding content to it like this : public func PaintInitialScreen () { let label = NSTextField(labelWithString: "TW window") label.frame = NSRect(x: 100, y: 200, width: 200, height: 200) // Adding content to viewcontroller TWStaticContext.sViewController.view.addSubview(label) } We are using this approach because we have a contraint in our application that we have to update UI programatically and on compile time we dont know what we want to show . We will be adding content on runtime based on how many button we want, what label we want , where to place it etc. When we were using purely appKit application, doing things programatically was simple but since SwiftUI is a declarative application we have to use above approach. Rational for shifting to SwiftUI entry point is that we want our application to be future safe and since apple is more inclined to SwiffUI, we want to design our entry flow to use SwiftUI entry point . And SwiftUI being declarative, we are using appKit to add content to hiracrchy programtically. We have used similar apprach in iOS also , where are using UIApplicationDelegateAdaptor inplace of NSApplicationAdaptor . And UIViewControllerReprestable in place of NSViewControllerRepresentable. Is this right approach to use ?
1
0
351
2w
Moving SceneDelegate to a different target
I have a SwiftUI project which has the following hierarchy: IOSSceneDelegate (App target) - depends on EntryPoint and Presentation static libs. Presentation (Static library) - Depends on EntryPoint static lib. Contains UI related logic and updates the UI after querying the data layer. EntryPoint (Static library) - Contains the entry point, AppDelegate (for its lifecycle aspects) etc. I've only listed the relevant targets here. SceneDelegate was initially present in EntryPoint library, because the AppDelegate references it when a scene is created. public func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Set the SceneDelegate dynamically let sceneConfig: UISceneConfiguration = UISceneConfiguration(name: "mainWindow", sessionRole: connectingSceneSession.role) sceneConfig.delegateClass = SceneDelegate.self return sceneConfig } The intent is to move the SceneDelegate to the Presentation library. When moved, the EntryPoint library fails to compile because it's referencing the SceneDelegate (as shown above). To remove this reference, I tried to set up the SceneDelegate in the old way - In the info.plist file, mention a SceneConfiguration and set the SceneDelegate in Presentation. // In the Info.plist file <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <true/> <key>UISceneConfigurations</key> <dict> <key>UIWindowSceneSessionRoleApplication</key> <array> <dict> <key>UISceneConfigurationName</key> <string>Default Configuration</string> <key>UISceneDelegateClassName</key> <string>Presentation.SceneDelegate</string> </dict> </array> </dict> </dict> // In the AppDelegate public func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Refer to a static UISceneconfiguration listed in the info.plist file return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } As shown above, the Presentation.SceneDelegate is referred in the Info.plist file and the reference is removed from the AppDelegate (in EntryPoint library). The app target compiles, but when I run it, the SceneDelegate is not invoked. None of the methods from the SceneDelegate (scene(_:willConnectTo:options:), sceneDidDisconnect(_:), sceneDidEnterBackground(_:) etc.) are invoked. I only get the AppDelegate logs. It seems like the Configuration is ignored because it was incorrect. Any thoughts? Is it possible to move the SceneDelegate in this situation?
0
1
268
2w
EntityAction for MaterialBaseTint - incorrect colours
Hello, I'm writing an EntityAction that animates a material base tint between two different colours. However, the colour that is being actually set differs in RGB values from that requested. For example, trying to set an end target of R0.5, G0.5, B0.5, results in a value of R0.735357, G0.735357, B0.735357. I can also see during the animation cycle that intermediate actual tint values are also incorrect, versus those being set. My understanding is the the values of material base colour are passed as a SIMD4. Therefore I have a couple of helper extensions to convert a UIColor into this format and mix between two colours. Note however, I don't think the issue is with this functions - even if their outputs are wrong, the final value of the base tint doesn't match the value being set. I wondered if this was a colour space issue? import simd import RealityKit import UIKit typealias Float4 = SIMD4<Float> extension Float4 { func mixedWith(_ value: Float4, by mix: Float) -> Float4 { Float4( simd_mix(x, value.x, mix), simd_mix(y, value.y, mix), simd_mix(z, value.z, mix), simd_mix(w, value.w, mix) ) } } extension UIColor { var float4: Float4 { var r: CGFloat = 0.0 var g: CGFloat = 0.0 var b: CGFloat = 0.0 var a: CGFloat = 0.0 getRed(&r, green: &g, blue: &b, alpha: &a) return Float4(Float(r), Float(g), Float(b), Float(a)) } } struct ColourAction: EntityAction { let startColour: SIMD4<Float> let targetColour: SIMD4<Float> var animatedValueType: (any AnimatableData.Type)? { SIMD4<Float>.self } init(startColour: UIColor, targetColour: UIColor) { self.startColour = startColour.float4 self.targetColour = targetColour.float4 } static func registerEntityAction() { ColourAction.subscribe(to: .updated) { event in guard let animationState = event.animationState else { return } let interpolatedColour = event.action.startColour.mixedWith(event.action.targetColour, by: Float(animationState.normalizedTime)) animationState.storeAnimatedValue(interpolatedColour) } } } extension Entity { func updateColour(from currentColour: UIColor, to targetColour: UIColor, duration: Double, endAction: @escaping (Entity) -> Void = { _ in }) { let colourAction = ColourAction(startColour: currentColour, targetColour: targetColour, endedAction: endAction) if let colourAnimation = try? AnimationResource.makeActionAnimation(for: colourAction, duration: duration, bindTarget: .material(0).baseColorTint) { playAnimation(colourAnimation) } } } The EntityAction can only be applied to an entity with a ModelComponent (because of the material), so it can be called like so: guard let modelComponent = entity.components[ModelComponent.self], let material = modelComponent.materials.first as? PhysicallyBasedMaterial else { return } let currentColour = material.baseColor.tint let targetColour = UIColor(_colorLiteralRed: 0.5, green: 0.5, blue: 0.5, alpha: 1.0) entity.updateColour(from:currentColour, to: targetColour, duration: 2)
1
0
392
2w
Performing simulations in the UI elements in uikit
I wanted to perform simulation in my application as a self tour guide for my user. For this I want to programatically simulate various user interaction events like button click, keypress event in the UITextField or moving the cursor around in the textField. These are only few examples to state, it can be any user interaction event or other events. I wanted to know what is the apple recommendation on how should these simulations be performed? Is there something that apple offers like creating an event which can be directly executed for simulations. Is there some library available for this purpose?
2
0
230
1w
ContactAccessPicker crashing issue
Given Apple's new .limited contact authorization introduced in ios18, I want to be able to present the ContactAccessPicker directly from my app, via ionic capacitor. I present the .contactAccessPicker view via a UIHostingController, and I manage the view controller's dismissal accordingly when the ContactAccessPicker completes and is no longer presented. Bug: After a few searches or interactions with the Contact Access Picker (ex. searching, selecting contacts, clicking the "show selected" button), the contact access picker crashes and the overlay remains. Any interaction with my app is then blocked because I can't detect that the contact access picker has disappeared when it crashes so I can't dismiss the viewController. Is there a way for me to prevent the contact access picker from crashing, and how can I detect if it does crash, so I can at least dismiss the view controller if that happens? struct ContactAccessPickerView: View { @Binding var isPresented: Bool let completion: @MainActor ([String]) -> Void var body: some View { Group { if #available(iOS 18.0, *) { Color.clear .contactAccessPicker(isPresented: $isPresented) { result in Task { @MainActor in completion(result) } } } else { } } } } @objc func selectMoreContacts(_ call: CAPPluginCall) { guard isContactsPermissionGranted() else { call.resolve(["success": false]) return } // Save the call to ensure it's available until we finish self.bridge?.saveCall(call) DispatchQueue.main.async { [weak self] in guard let self = self else { return } var isPresented = true let picker = ContactAccessPickerView(isPresented: .init(get: { isPresented }, set: { isPresented = $0 })) { contacts in call.resolve(["success": true]) self.dismissAndCleanup(call) } let hostingController = UIHostingController(rootView: picker) hostingController.modalPresentationStyle = .overFullScreen self.bridge?.viewController?.present(hostingController, animated: true) } }
0
2
252
2w
Setting rounded corners via CAShapeLayer.path looks problematic
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white do { let shapeLayer = CAShapeLayer() shapeLayer.frame = CGRect(x: 50, y: 100, width: 200, height: 108) let path = UIBezierPath(roundedRect: shapeLayer.bounds, cornerRadius: 36) shapeLayer.path = path.cgPath shapeLayer.fillColor = UIColor.orange.cgColor view.layer.addSublayer(shapeLayer) } do { let layer = CALayer() layer.backgroundColor = UIColor.blue.cgColor layer.cornerRadius = 36 layer.frame = CGRect(x: 50, y: 300, width: 200, height: 108) view.layer.addSublayer(layer) } } } The corner radius is set to 36 through CAShapeLayer, but the actual effect is larger than 36, close to half of the height. Setting it through CALayer is fine Can anyone explain it to me? Thank you
1
0
209
2w
UIDocumentPickerViewController of type pdf cannot pick pdf in simulator
I'm having this problem, with this code: let docPicker = UIDocumentPickerViewController(forOpeningContentTypes: [ .pdf ]) docPicker.delegate = self docPicker.modalPresentationStyle = .currentContext view.window?.rootViewController?.present(docPicker, animated: true, completion: nil) but then when I open the simulator and click the button that calls to the method that has this code... Cannot pick the pdf document. Testing in browserstack with real devices is working, but it's a very slow process, why I cannot use simulators to make work this?
1
0
211
2w
Memory Leak in Apple TV(tvOS 17.4)
I found a memory leak in tvOS 17.4, but it's not happening in tvOS 18.0 here is the code flow 1.I have controller inside which I have tableView which in turn contains a collectionview here I have passed self to tableViewcell as delegate and then from tableview cell I have passed self again as delegate to collectionViewcell, but memory is not released, because self is retained "I have passed self as weak every where still memory leak is happening only in tvOS 17.4 and below versions. but in 18.0 and above versions it's fine"
1
0
237
2w
iPAD custom in app keyboard stopped responding
IOS 12 - 18.1.1 - objective C, Xcode 16.0 App runs on both iPhone and iPad, this issue only occurs happens on iPads. For the iPhones I am able to get a decent numeric only keyboard to display. I pulled down NumericKeypad from GitHub and used that a model on how to implement a custom keypad. In the inputView of the delegate, a new custom text field is create and then assigned a delegate and other properties then it returns the view to the main ViewController. When the ViewControllers and the correct text field is entered my custom keyboard display and the buttons respond but nothing is displayed in the text field. This has worked for years and all of the sudden it stopped. The original project for the example 10 key custom keyboard builds and when loaded that works on the iPad. If I comment out condition to only execute if running on an iPad and test with an iPhone the keyboards works. It is only on a iPad that this happens. This is the cod that creates creates the keyboard from a .xib file. I am using a storyboard for the main app. #import "Numeric10KeyTextField.h" #import "Numeric10KeyViewController.h" @implementation Numeric10KeyTextField (UIView *)inputView { UIView *view = nil; Numeric10KeyViewController *numVC; // Add hook here for iPhone or other devices if needed but now return nil if iPhone so it uses the default // if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { numVC = [[Numeric10KeyViewController alloc] initWithNibName:@"Numeric10Key" bundle:nil]; [numVC setActionSubviews:numVC.view]; numVC.delegate = self.numeric10KeyDelegate; numVC.numpadTextField = self; view = numVC.view; // } return view; } @end
1
0
273
1w
swiftui fileimporter inside UIHostingController
I'm working on an old iOS app that started with objective-C + UIKit and has being migrated to Swift + SwiftUI. Currently its code is mostly Swift + SwiftUI but it has still some objective-C and some UIKit ViewControllers. One of the SwiftUI views uses fileImporter to open Files App and select a file from the device. This has been working well until iOS 18 is launched. With iOS 18 the file picker is not launching correctly and is frozen in every simulator (the unique real device I've could test with iOS 18 seemed to work correctly). I managed to clone my project and leave it with the minimal amount of files to reproduce this error. This is the code: AppDelegate.h #import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate> {} @property (strong, nonatomic) UIWindow *window; @end AppDelegate.m #import "AppDelegate.h" #import "MyApp-Swift.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; FirstViewBuilder *viewBuilder = [[FirstViewBuilder alloc] init]; [viewBuilder show]; return YES; } @end FirstViewBuilder.swift import SwiftUI @objc class FirstViewBuilder: NSObject { private var view: UIHostingController<FirstView> @objc override init() { self.view = MyHostingController(rootView: FirstView()) } @objc func show() { let app = UIApplication.shared.delegate as? AppDelegate let window = app?.window window?.backgroundColor = .white // Use navigationController or view directly depending on use window?.rootViewController = view } } FirstView.swift import SwiftUI struct FirstView: View { @State var hasToOpenFilesApp = false var body: some View { VStack(alignment: .leading, spacing: 0) { Button("Open Files app") { hasToOpenFilesApp = true }.fileImporter(isPresented: $hasToOpenFilesApp, allowedContentTypes: [.text]) { result in switch result { case .success(let url): print(url.debugDescription) case .failure(let error): print(error.localizedDescription) } } } } } And finally, MyHostingController import SwiftUI class MyHostingController<Content>: UIHostingController<Content> where Content: View { override init(rootView: Content) { super.init(rootView: rootView) } @objc required dynamic init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() navigationItem.hidesBackButton = true } } Launching this in an iPhone 13 Pro (18.2) simulator I click on Open Files App, it takes 2 seconds to open it, and it opens full screen (not like a modal). Buttons on the top are behind the status bar and buttons at the bottom are behind the Home indicator. But it's worse because the user can't interact with this view, it's frozen. I created a fresh SwiftUI project just with this unique view and the fileimport worked as expected so I thought the problem was due to embed the SwiftUI view inside the UIHostingController. So I made these modifications to the minimal project: Remove the files AppDelegate, FirstViewBuilder and MyHostingController. Create this SwiftUI App file import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { FirstView() } } } And again the same problem with iOS 18. But if I launch this exact project in an iPhone 13 Pro (17.4) simulator and open the files apps (now it opens almost instantly) it works OK and shows the file picker as a modal, as expected, and I can interact with it and select files. Last thing I've tried is removing LaunchScreen.xib from my project and Launch screen interface file base name key from my info.plist but the problem keeps happening. I guess it must be due to my project configuration (too old) but I have no more ideas of where to look at. The possibility of having a fresh SwiftUI project and "move" the old project to the new one could take me several weeks and I discard it by the moment. Could I use another method to select files from SwiftUI views with iOS 18?
2
0
314
2w
SwiftUI navigationDestination will make child view's stateObject init multi times with sheet modifier.
Below is my sample code. On the Home page, when I click "show sheet," the sheet page expands, and the StateObject inside the sheet is initialized once. However, when I click "show Fullscreen" and then click "show sheet" inside the fullscreen page, the sheet gets initialized twice. However, if I remove navigationDestination, this issue does not occur. This problem causes the network request in the sheet page to be triggered multiple times. Can someone tell me the reason? enum TestRouter: String, Hashable { case test var targetView: some View { Text("test") } var title: String { return "test title" } } @MainActor struct NavigationInnerView<Content>: View where Content: View { var contentView: () -> Content @MainActor public init(@ViewBuilder contentView: @escaping () -> Content) { self.contentView = contentView } var body: some View { NavigationStack() { contentView() .navigationDestination(for: TestRouter.self) { route in route.targetView } } .navigationViewStyle(StackNavigationViewStyle()) } } struct ContentView: View { @State var showFullScreen: Bool = false @State var showSheet: Bool = false var contentView: some View { VStack { VStack { Text("Home") Button { showFullScreen = true } label: { Text("show fullscreen") } Button { showSheet = true } label: { Text("show sheet ") } } } } var body: some View { NavigationInnerView { contentView .fullScreenCover(isPresented: $showFullScreen) { NavigationInnerView { FullScreenContentView() } } .sheet(isPresented: $showSheet) { NavigationInnerView { SheetContentView() } } } } } class FullScreenViewModel: ObservableObject { @Published var content: Bool = false init() { print("Full Screen ViewModel init") } } struct FullScreenContentView: View { @Environment(\.dismiss) var dismiss @State var showSheet: Bool = false @StateObject var viewModel: FullScreenViewModel = .init() init() { print("Full screen view init") } var body: some View { VStack { Text("FullScreen") Button { dismiss() }label: { Text("dismiss") } Button { showSheet = true } label: { Text("show sheet") } } .sheet(isPresented: $showSheet) { NavigationInnerView { SheetContentView() } } } } class SheetViewModel: ObservableObject { @Published var content: Bool = false init() { print("SheetViewModel init") } } struct SheetContentView: View { @Environment(\.dismiss) var dismiss @StateObject var viewModel = SheetViewModel() init() { print("sheet view init") } var body: some View { Text("Sheet") Button { dismiss() } label: { Text("dismiss") } } } #Preview { ContentView() }
3
0
228
2w
CollectionView: Sync `center.y` of UIView outside collection view and a view in a cellinside
I have a setup: Collection view with compositional layout a self sizing cell inside a subview inside the cell and unrelated view outside the collection view I would like to: modify the layout (constraints) of the cell inside the collection view with UIView.animate trigger an animated layout update of collection view synchronize the position of an unrelated view to the position of one of the subviews of a collection view cell What I tried: UIView.animate(withDuration: 0.25) { cellViewReference.updateState(state: state, animated: false) collectionView.collectionViewLayout.invalidateLayout() collectionView.layoutIfNeeded() someOtherViewOutsideCollectionView.center = cellViewReference.getPositionOfThatOneViewInWindowCoordinateSystem() } What I'm expecting: after invalidateLayout, the layout update of the collection view is merely scheduled, but not yet performed layoutIfNeeded forces an update on the collectionViewLayout + update on the frames of the views inside the UICollectionViewCells all the frames become correct to what they will look like after the animation is performed I call getPositionOfThatOneViewInWindowCoordinateSystem and it gives me the position of the view after the uicollectionview AND the cell's layout has updated What happens instead: getPositionOfThatOneViewInWindowCoordinateSystem returns me an old value I am observing that the bounds of the cell didn't actually change during layoutIfNeeded And moreover, the bounds change without animation, instantly Question: how to animate self sizing cell size change due relayout how to synchronize outside views with collection views
1
0
202
2w
UICollectionView.CellRegistration and dequeueConfiguredReusableCell with nil for Item? causes runtime crash
Modern collection views use UICollectionViewDiffableDataSource with UICollectionView.CellRegistration and UICollectionView.dequeueConfiguredReusableCell(registration:indexPath:item). There are runtime crashes when passing nil as argument for the item parameter. There's no clear documentation on whether optional items are allowed or not. The function signature in Swift is: @MainActor @preconcurrency func dequeueConfiguredReusableCell<Cell, Item>(using registration: UICollectionView.CellRegistration<Cell, Item>, for indexPath: IndexPath, item: Item?) -> Cell where Cell : UICollectionViewCell Given the Item? type one would assume Optionals are allowed. In Objective-C the signature is: - (__kindof UICollectionViewCell *)dequeueConfiguredReusableCellWithRegistration:(UICollectionViewCellRegistration *)registration forIndexPath:(NSIndexPath *)indexPath item:(id)item; I'm not sure, if there's implicit nullability bridging to the Swift API or if the Objective-C files has some explicit nullability annotation. The crash is due to a swift_dynamicCast failing with: Could not cast value of type '__SwiftNull' (0x10b1c4dd0) to 'Item' (0x10d6086e0). It's possible to workaround this by making a custom Optional type like enum MyOptional<T> { case nothing case something(T) } and then wrapping and unwrapping Item? to MyOptional<Item>. But this feels like unnecessary boilerplate. With the current situation it's easy to ship an app where everything seems to work, but in production only certain edge cases cause nil values being used and then crashing the app. Please clarify the allowed arguments / types for the dequeueConfiguredReusableCell function. Either Optionals should be supported and not crash at runtime or the signatures should be changed so there's a compile time error, when trying to use an Item?. Feedback: FB16494078
3
1
217
1w
SFSafariViewController's preferred colors are invalidated after rotation
There are two issues about SFSafariViewController. After rotate from landscape to portrait, The topAnchor is destroyed. The specified bar tint color and control tint color are invalidated.(Returns to system color) Regarding the second issue, I’ve found a temporary workaround. Override the viewWillTransition(to:with:) and keep it empty. Don't call super.viewWillTransition(to:with:). Since UIKit is not open source, I don’t know the exact cause, but I found something that could be the key to the issue. So, I reported it to Apple Feedback Assistant. You can check the details and the sample project in the GitHub repository below. https://github.com/ueunli/SafariViewer
0
0
188
3w