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

How do I make a toolbar-style window ornament from UIKit?
I've got a UIKit app and I want to add some buttons in a top-edge window ornament. I'm looking at the WWDC23 talk Meet UIKit for Spatial Computing, and it does exactly what I think I want to do: extension EditorViewController { func showEditingControlsOrnament() { let ornament = UIHostingOrnament(sceneAlignment: .bottom, contentAlignment: .center) { EditingControlsView(model: controlsViewModel) .glassBackgroundEffect() } self.ornaments = [ornament] editorView.style = .edgeToEdge } } But the thing I really want to know is what is in EditingControlsView. Is it a toolbar? How do you make a toolbar in SwiftUI without something to attach the .toolbar modifier to?
1
0
162
4w
PIP not working properly with Xcode16 on iOS18
I am trying to build my app with Xcode16 on iOS18, once app is going background PIP is enable but whenever app is coming back to foreground by tapping on PIP, black screen appears(video is playing in background). It only happening with build created by Xcode16 and running on iOS18. It working fine with build created by Xcode15.4. My first thought on it that somehow PIP is not stoping.
1
0
157
Oct ’24
UIImage causes memory to run out
I have a project that currently has data saved locally and I'm trying to get it to sync over multiple devices. Currently basic data is syncing perfectly fine, but I'm having issues getting the images to convert to data. From what I've researched it because I'm using a UIImage to convert and this caches the image It works fine when there's only a few images, but if there's several its a pain The associated code func updateLocalImages() { autoreleasepool { let fetchRequest: NSFetchRequest<Project> = Project.fetchRequest() fetchRequest.predicate = NSPredicate(format: "converted = %d", false) fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Project.statusOrder?.sortOrder, ascending: true), NSSortDescriptor(keyPath: \Project.name, ascending: true)] do { let projects = try viewContext.fetch(fetchRequest) for project in projects { currentPicNumber = 0 currentProjectName = project.name ?? "Error loading project" if let pictures = project.pictures { projectPicNumber = pictures.count for pic in pictures { currentPicNumber = currentPicNumber + 1 let picture : Picture = pic as! Picture if let imgData = convertImage(picture: picture) { picture.pictureData = imgData } } project.converted = true saveContext() } } } catch { print("Fetch Failed") } } } func convertImage(picture : Picture)-> Data? { let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) let path = paths[0] if let picName = picture.pictureName { let imagePath = path.appendingPathComponent(picName) if let uiImage = UIImage(contentsOfFile: imagePath.path) { if let imageData = uiImage.jpegData(compressionQuality: 0.5) { return imageData } } } return nil }```
2
0
190
3w
UITextField doesn't apply `keyboardType` on tvOS because of `isSecureTextEntry`
Hello everyone, Recently, I have encountered an issue in my tvOS app where a specific property of UITextField, isSecureTextEntry, set to true, was preventing another property, keyboardType, from functioning correctly. In my case, keyboardType is set to numberPad option. The problem is that during the first tap on the text field, the default keyboard with numbers, letters, and some special characters opens. However, after the second tap, the correct keyboard type with only numbers appears as I want. Removing isSecureTextEntry or setting to false solves the problem. import UIKit class ViewController: UIViewController { private let textField = UITextField() override func viewDidLoad() { super.viewDidLoad() textField.keyboardType = .numberPad textField.isSecureTextEntry = true view.addSubview(textField) setupConstraints() } }
0
0
186
Oct ’24
Programmatically switch between "mirror" and "additional content" with windowExternalDisplayNonInteractive
I'd like to share an app's screen in two modes. First in a standard mirroring mode and second in an "additional content" mode (very likely with a session role windowExternalDisplayNonInteractive). I found that the Keynote app on iOS does a very nice example of what I want to achieve when sharing an iPhone using AirPlay to an AppleTV. sharing a screen results in mirroring the screen on the TV tapping the play button in Keynote switches to "additional content" where iPhone and TV show different content leaving the additional content mode returns to "mirroring" where TV and iPhone show the same content Is there an example for implementing such a feature? I am able to successfully use the external display (windowExternalDisplayNonInteractive) and show additional content there. How can I programmatically "detach" the additional content from the external display and activate mirroring mode? Searching the Developer Forums for windowExternalDisplayNonInteractive reveals some discussions, which include valuable information, however, returning to mirror mode does not seem to be covered.
1
0
172
Oct ’24
Modal UINavigationController shown programmatically has no navigation buttons
When I create a modal segue to a navigation controller in a storyboard, the navigation bar buttons appear correctly. But when trying to recreate this programmatically, no buttons appear: import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let button = UIButton(type: .infoLight, primaryAction: UIAction(handler: { _ in self.present(UINavigationController(rootViewController: ModalViewController()), animated: true) })) button.frame.origin = CGPoint(x: 100, y: 100) view.addSubview(button) } } class ModalViewController: UIViewController { override func loadView() { let button = UIBarButtonItem(title: "button") button.primaryAction = UIAction(handler: { action in }) button.style = .done navigationItem.title = "title" navigationItem.rightBarButtonItem = button view = UITableView() } } What am I doing wrong?
2
0
182
Oct ’24
Crash when changing the accessibilityElements to custom UIAccessibilityElement
I got a UIControl, and I want to make it behavior like a custom UIAccessibilityElement. UIControl *control = [[UIControl alloc] init]; control.isAccessibilityElement = NO; CustomAccessibilityElement *elem = [[CustomAccessibilityElement alloc] initWithAccessibilityContainer:control]; elem.isAccessibilityElement = YES; // some custom setting here control.accessibilityElements = @[elem]; It worked well with an iPhone 13, iOS 15.5.1, but crashed with an iPhone SE, iOS 15.4.1 and the crash msg is : "-[UIAccessibilityElement _addAccessibilityElementsAndOrderedContainersWithOptions:toCollection:]: unrecognized selector sent to instance 0x283b7c680" Can you tell me the reason ? Thanks a lot.
1
0
215
Oct ’24
iPadOS crashes: UpdateCoalescingCollectionView is stuck in a recursive layout loop since iOS 18.0.1
Hello! Since iOS 18.0.1 I receive crash reports for iPadOS only, "Fatal error: <UpdateCoalescingCollectionView 0x30148e7c0> is stuck in a recursive layout loop. The error is not reproducible for me. However, the screen where the user did the last touch event has no CollectionView but just a ScrollView. Can it be possible that this error can happen on a View that is not visible but is on another tab of the active UITabViewController(Representable) but has a UICollectionView? Or can you see somehow from the crash report on which SwiftIUView/UIKItView this error happened? crash_report.crash
0
1
238
Oct ’24
How to get UIFont to respect preferredContentSizeCategory in a Mac Catalyst app?
I have an iOS app that relies on dynamic text size such that all fonts in the app respect the user's setting of Text Size in the iOS Settings app. This app also runs on macOS via Mac Catalyst. But until macOS 14 Sonoma, there was no Text Size setting in the macOS Settings app. But even as of Sonoma, the Text Size setting isn't usable by 3rd party apps. And Sequoia doesn't seem to change that. As a work around, my Mac Catalyst app provides its own Text Size setting. I was able to make it work by providing my own UIApplication subclass and overriding preferredContentSizeCategory. Under macOS 12 to macOS 14, this workaround works just fine and all fonts in the app created with code such as UIFont.preferredFont(forTextStyle:) gives appropriately sized fonts based on the overridden content size category. However, this workaround stopped working with macOS 15 Sequoia. I've also tried code such as: self.window.traitOverrides.preferredContentSizeCategory = myCustomSizeCategoryValue and self.window.maximumContentSizeCategory = myCustomSizeCategoryValue self.window.minimumContentSizeCategory = myCustomSizeCategoryValue in the scene delegate but that made no difference. Is there any way to get code such as UIFont.preferredFont(forTextStyle:) to return an appropriately sized font based on some app provided content size category in a Mac Catalyst app running under macOS 15? It sure would be nice if Mac Catalyst apps automatically responded to the macOS Text Size setting under Settings -> Accessibility -> Display -> Text Size just like a native iOS app.
2
0
285
Oct ’24
Wallpaper Api
Hey, I have an app that user selects wallpaper for iPhone. I want a feature that user can set wallpaper direct from app itself for lock screen and home screen not download the image and manually set the wallpaper. As my research there was a PhotoLibrary api that contains PLWallpaperImageViewController.h which allows you to set wallpaper directly. Thank You!
1
0
300
Oct ’24
How to remove antialiasing in CGContext?
I'm trying to create a brush by drawing in CGContext using a UIImage for a brush. However, I when I try drawing, the stroke is antialiased when I don't want it to be. I tried context.interpolationQuality = .none context.setShouldAntialias(false) but it doesn't seem to work. Is it a problem with my brush or resizing the brush maybe?? (Also this problem doesn't occur when I draw a regular stroke without the brush.) Any help or advice would be greatly appreciated! Here is my draw function override func draw(_ rect: CGRect) { super.draw(rect) guard let context = UIGraphicsGetCurrentContext() else { return } context.interpolationQuality = .none context.setShouldAntialias(false) for stroke in strokes { let brush = UIColor.blue.circle(size: CGSize(width: CGFloat(stroke.width * 10), height: CGFloat(stroke.width * 10))).mask(color: UIColor(cgColor: stroke.color)) var first = true for point in stroke.points { if first { first = false context.move(to: point) continue } context.addLine(to: point, using: brush) } } } Circle brush extension UIColor { func circle(size: CGSize = CGSize(width: 1, height: 1)) -> UIImage { return UIGraphicsImageRenderer(size: size).image { rendererContext in self.setFill() UIBezierPath(ovalIn: CGRect(origin: .zero, size: size)).fill() } } } CGConext extension where I overloaded addLine extension CGContext { func addLine(to point: CGPoint, using brush: UIImage, density: CGFloat = 1.0) { var frame: CGRect = .zero frame.size = brush.size let lastPoint = self.currentPointOfPath let distanceX = point.x - lastPoint.x let distanceY = point.y - lastPoint.y let distanceR = sqrt(pow(distanceX, 2) + pow(distanceY, 2)) let deltaR = (1.0 / density) let numOfSteps = ceil(distanceR / deltaR) var renders : CGFloat = 0.0 let deltaX = distanceX / numOfSteps let deltaY = distanceY / numOfSteps var currentCenter = lastPoint repeat { frame.origin.x = currentCenter.x - frame.width / 2.0 frame.origin.y = currentCenter.y - frame.height / 2.0 brush.draw(in: frame) currentCenter.x += deltaX currentCenter.y += deltaY renders += 1.0 } while (renders <= numOfSteps) self.move(to: point) } }
4
0
240
Oct ’24
UICollectionView Move Item Method Not Called in iOS 18
Summary In iOS 18, the UICollectionViewDelegate method collectionView(_:targetIndexPathForMoveOfItemFromOriginalIndexPath:atCurrentIndexPath:toProposedIndexPath:) is not being called when moving items in a UICollectionView. This method works as expected in iOS 17.5 and earlier versions. Steps to Reproduce Create a UICollectionView with drag and drop enabled. Implement the UICollectionViewDelegate method: func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveOfItemFromOriginalIndexPath originalIndexPath: IndexPath, atCurrentIndexPath currentIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath { print("🐸 Move") return proposedIndexPath } Run the app on iOS 18. Attempt to drag and drop items within the collection view. Expected Behavior The method should be called during the drag and drop operation, and "🐸 Move" should be printed to the console. Actual Behavior The method is not called, and nothing is printed to the console. The drag and drop operation still occurs, but without invoking this delegate method. Configuration iOS Version: 18 Xcode Version: Xcode 16.0.0
1
1
197
Oct ’24
iOS 18 tabbar flickers and then disappears
Use a UITabBarController to load two ViewControllers, A and B, both wrapped in UINavigationController. In A’s viewDidLoad method, push to C, with C’s hidesBottomBarWhenPushed set to true. When popping back to A, the tab bar flickers and then disappears. In versions lower than iOS 18, after popping back, the tabBar does not flicker and disappear. A ViewController override func viewDidLoad() { super.viewDidLoad() let vc = CViewController() vc.hidesBottomBarWhenPushed = true navigationController?.pushViewController(vc, animated: true) // Do any additional setup after loading the view. }
2
0
333
Oct ’24
trailingSwipeActionsConfigurationProvider causes shadow effect on UICollectionViewListCell gone
Currently, I have achieve shadow and corner effect for UICollectionViewListCell, using the following code. UICollectionViewListCell class NoteCell: UICollectionViewListCell { override func awakeFromNib() { super.awakeFromNib() initShadow() initCorner() } private func updateShadowColor() { // Determine the shadow color based on the current interface style let shadowUIColor = UIColor.label self.layer.shadowColor = shadowUIColor.cgColor } private func initShadow() { // https://www.hackingwithswift.com/example-code/uikit/how-to-add-a-shadow-to-a-uiview self.layer.shadowOpacity = 0.3 self.layer.shadowOffset = CGSize(width: 0.5, height: 0.5) self.layer.shadowRadius = 2 self.layer.masksToBounds = false self.updateShadowColor() // Remove the following two lines if you experience any issues with shadow rendering: self.layer.shouldRasterize = true self.layer.rasterizationScale = UIScreen.main.scale } private func initCorner() { var backgroundConfig = UIBackgroundConfiguration.listPlainCell() backgroundConfig.backgroundColor = .systemBackground backgroundConfig.cornerRadius = 16 self.backgroundConfiguration = backgroundConfig } layout private func layoutConfig() -> UICollectionViewCompositionalLayout { let layout = UICollectionViewCompositionalLayout { section, layoutEnvironment in var config = UICollectionLayoutListConfiguration(appearance: .plain) config.headerMode = .none config.footerMode = .none config.showsSeparators = false config.headerTopPadding = 0 config.backgroundColor = nil config.trailingSwipeActionsConfigurationProvider = { [weak self] indexPath in guard let self = self else { return nil } // Knowing what we are tapping at. var snapshot = dataSource.snapshot() let sectionIdentifier = snapshot.sectionIdentifiers[indexPath.section] let itemIdentifiers = snapshot.itemIdentifiers(inSection: sectionIdentifier) let itemIdentifier: NoteWrapper = itemIdentifiers[indexPath.item] let deleteHandler: UIContextualAction.Handler = { action, view, completion in completion(true) // TODO: //snapshot.reloadItems([itemIdentifier]) } let deleteAction = UIContextualAction(style: .normal, title: "Trash", handler: deleteHandler) var swipeActionsConfiguration = UISwipeActionsConfiguration(actions: [ deleteAction, ]) deleteAction.image = UIImage(systemName: "trash") deleteAction.backgroundColor = UIColor.systemRed swipeActionsConfiguration.performsFirstActionWithFullSwipe = false return swipeActionsConfiguration } // https://developer.apple.com/forums/thread/759987 let layoutSection = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment) layoutSection.interGroupSpacing = 16 // Distance between item. layoutSection.contentInsets = NSDirectionalEdgeInsets( top: 16, // Distance between 1st item and its own header. leading: 16, bottom: 16, // Distance of last item and other header/ bottom edge. trailing: 16 ) return layoutSection } return layout } This is the outcome. However, when I perform swipe action, the shadow effect is gone. Do you have any idea how I can resolve such? Thanks.
0
0
180
Oct ’24
Get View Full HDR state from Settings > Photos to properly set preferredImageDynamicRange in editing extension
I'm updating my Photo Editing Extension to support HDR. To do this I set imageView.preferredImageDynamicRange = .high. But you can turn off the option to view HDR photos in the complete dynamic range in Settings > Photos. When you do that, open a photo, and tap the edit button, it does not appear in the full range as expected, but when you select my app from More > Extensions, it does appear in the complete dynamic range unexpectedly. I need to set imageView.preferredImageDynamicRange = .standard when View Full HDR is off, but I don't see any way to get that in my PHContentEditingController.
1
0
325
Oct ’24
How to connect SwiftUI state with UITable?
I use @Binding to sync data between SwiftUI component state and UITable state. my observations: while reordering @State variable of the TestView is updating, it can be checked by taping the button inside the table the data is not updating. 'log2' is always the same on reorder and which is more important the app crashes when I try to remove item. What to I missing? import SwiftUI import UIKit struct TestView: View { @State var items = ["item 1", "item 2", "item 3", "item 4", "item 5"] var body: some View { VStack { Button("Print Items") { print("log1", items) } ReorderableListView( items: $items ) { item in Text(item) } } } } private struct ReorderableListView<Content: View>: UIViewControllerRepresentable { @Binding var items: [String] let content: (String) -> Content class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate { var parent: ReorderableListView init(parent: ReorderableListView) { self.parent = parent } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { print("log2", parent.items) return parent.items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) let hostingController = UIHostingController(rootView: parent.content(parent.items[indexPath.row])) hostingController.view.backgroundColor = .clear cell.contentView.subviews.forEach { $0.removeFromSuperview() } cell.contentView.addSubview(hostingController.view) cell.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) if indexPath.row == self.parent.items.count - 1 { cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: .greatestFiniteMagnitude) } hostingController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: cell.contentView.topAnchor), hostingController.view.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor), hostingController.view.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor) ]) return cell } func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { let movedObject = parent.items.remove(at: sourceIndexPath.row) parent.items.insert(movedObject, at: destinationIndexPath.row) } func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { return true } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { parent.items.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .automatic) } } } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } func makeUIViewController(context: Context) -> UITableViewController { let tableViewController = UITableViewController() tableViewController.tableView.dataSource = context.coordinator tableViewController.tableView.delegate = context.coordinator tableViewController.tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "Cell") tableViewController.tableView.isEditing = true return tableViewController } func updateUIViewController(_ uiViewController: UITableViewController, context: Context) { uiViewController.tableView.reloadData() } } private class CustomTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } override func layoutSubviews() { super.layoutSubviews() superview?.subviews.filter({ "\(type(of: $0))" == "UIShadowView" }).forEach { (sv: UIView) in sv.removeFromSuperview() } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } #Preview { TestView() }
1
0
193
Oct ’24
UISearchBar Warnings in Xcode Version 16.0 w/ iOS 18.0
Explanation: I am working on an application and had trouble diagnosing the warnings below. I pulled the search bar code out into a separate project, and the warnings still show up. The entire code for the sample project is shown below. The "AddInstanceForFactory", "LoudnessManager", and "NSBundle" warning all show up immediately after tapping the search bar. The "RTIInputSystemClient" warning arises after tapping, dismissing, and re-tapping the search bar. Any help with resolving these warnings is appreciated! Warnings: "-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID. inputModality = Keyboard, inputOperation = , customInfoType = UIEmojiSearchOperations" "NSBundle file:///Library/Developer/CoreSimulator/Volumes/iOS_22A3351/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS%2018.0.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/MetalTools.framework/ principal class is nil because all fallbacks have failed" "AddInstanceForFactory: No factory registered for id <CFUUID --------------> -----------------" "LoudnessManager.mm:709 unable to open stream for LoudnessManager plist" Code: import UIKit class SearchView: UIView { private lazy var searchBar: UISearchBar = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.translatesAutoresizingMaskIntoConstraints = false searchBar.placeholder = "Search" return searchBar }() required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } init() { super.init(frame: .zero) translatesAutoresizingMaskIntoConstraints = false backgroundColor = .yellow addSubview(searchBar) NSLayoutConstraint.activate([ searchBar.topAnchor.constraint(equalTo: topAnchor, constant: 12), searchBar.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), searchBar.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12), searchBar.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12), ]) } } extension SearchView: UISearchBarDelegate { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { print(searchText) } } class ViewController: UIViewController { private lazy var searchView = SearchView() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .red let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) view.addGestureRecognizer(tap) view.addSubview(searchView) NSLayoutConstraint.activate([ searchView.leadingAnchor.constraint(equalTo: view.leadingAnchor), searchView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24), searchView.centerXAnchor.constraint(equalTo: view.centerXAnchor), ]) } @objc func dismissKeyboard() { view.endEditing(true) } }
0
0
380
Oct ’24