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.
How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here
UIKit
RSS for tagConstruct and manage graphical, event-driven user interfaces for iOS or tvOS apps using UIKit.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm trying to switch to UIKit's document lifecycle due to serious bugs with SwiftUI's version.
However I'm noticing the template project from Xcode isn't compatible with Swift 6 (I already migrated my app to Swift 6.). To reproduce:
File -> New -> Project
Select "Document App" under iOS
Set "Interface: UIKit"
In Build Settings, change Swift Language Version to Swift 6
Run app
Tap "Create Document"
Observe: crash in _dispatch_assert_queue_fail
Does anyone know of a work around other than downgrading to Swift 5?
Hi Everybody,
I am actually developing dynamic shortcuts for my app. I have a problem with the class UIApplicationShortcutIcon. When I pass a personalized icon in the parameter icon as a UIApplicationShortcutIcon(templateImageName: "nameOfTheAsset" I always visualize a black dot instead of my Icon.
The icon is imported as .SVG file and rendered as a template. Sincerely I do not know what to do to solve this problem since the documentation is little. Hoping somebody can give some tips to solve the problem
I have an iOS app where I'm trying to paste something previously copied to the user's UIPasteboard. I came across the UIPasteControl as an option for a user to tap to silently paste without having the prompt "Allow Paste" pop up.
For some reason, despite having what seemingly is the correct configurations for the UIPasteControl, on testing a tap, nothing is called. I expected override func paste(itemProviders: [NSItemProvider]) to fire, but it does not.
Any help would be appreciated as there doesn't seem to be much info anywhere regarding UIPasteControl.
import UniformTypeIdentifiers
class ViewController: UIViewController {
private let pasteControl = UIPasteControl()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
pasteControl.target = self
pasteConfiguration = UIPasteConfiguration(acceptableTypeIdentifiers: [
UTType.text.identifier,
UTType.url.identifier,
UTType.plainText.identifier
])
view.addSubview(pasteControl)
pasteControl.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
pasteControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
pasteControl.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
}
extension ViewController {
override func paste(itemProviders: [NSItemProvider]) {
for provider in itemProviders {
if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
provider.loadObject(ofClass: URL.self) { [weak self] reading, _ in
guard let url = reading as? URL else { return }
print(url)
}
}
else if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
provider.loadObject(ofClass: NSString.self) { [weak self] reading, _ in
guard let nsstr = reading as? NSString else { return }
let str = nsstr as String
if let url = URL(string: str) {
print(url)
}
}
}
}
}
}
I'm experimenting with RealityView in the UI of an AUv3 plug-in. The plug-in UI is implemented in a UIKitViewController with a UIHostingController hosting a RealityView. When i run the standalone app on visionOS I want the background to be transparent, and the reality view content. how can i achieve that? I've tried turning off opaque in many views and and setting background colors to .clear.
Description:
When initiating the print flow via UIPrintInteractionController, and no printer is initially connected, iOS displays all possible paper sizes in the paper selection UI. However, if a printer connects in the background after this view is shown, the list of paper sizes does not automatically refresh to reflect only the options supported by the connected printer.
If the user selects an incompatible paper size (one not supported by the printer that has just connected), the app crashes due to an invalid configuration.
Steps to Reproduce:
Launch the app and navigate to the print functionality.
Tap the Print button to invoke UIPrintInteractionController.
At this point, no printer is yet connected. iOS displays all available paper sizes.
While the paper selection UI is visible, the AirPrint-compatible printer connects in the background.
Without dismissing the controller, the user selects a paper size (e.g., one that is not supported by the printer).
The app crashes.
Expected Result: App should not crash
Once the printer becomes available (connected in the background), the paper size options should refresh automatically.
The list should be filtered to only include sizes that are compatible with the connected printer.
This prevents the user from selecting an invalid option, avoiding crashes.
Actual Result: App crashes
The paper size list remains unfiltered.
The user can still select unsupported paper sizes.
Selecting an incompatible option causes the app to crash, due to a mismatch between UI selection and printer capability.
Topic:
UI Frameworks
SubTopic:
UIKit
Hello,
I have a custom 3D object viewer on iOS that lets users spin the model using the touchscreen or a trackpad and supports coasting (momentum spinning). I need to stop the coasting animation as soon as the user touches down, but I can only immediately detect touches on the screen itself - on the trackpad I can't get an immediate notification of the touches.
So far I’ve tried:
State.began on my UIPanGestureRecognizer. It only fires after a small movement on both touchscreen and trackpad.
.possible on the pan gesture; this state never occurs during the gesture cycle.
UIApplicationSupportsIndirectInputEvents = YES in Info.plist; it didn’t make touchesBegan fire for indirectPointer touches.
Since UITableView (and other UIScrollView subclasses) clearly detect trackpad “touch-down” to cancel scrolling, there must be a way to receive that event. Does anyone know how to catch the initial trackpad contact—before any movement—on an indirect input device?
Below is a minimal code snippet demonstrating the issue. On the touchscreen you'll see a message the moment you touch the view, but the trackpad doesn't trigger any messages until your fingers move. Any advice would be greatly appreciated.
Thanks in advance,
John
import UIKit
class ViewController: UIViewController {
private let debugView = DebugView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// Fill the screen with our debug view
debugView.frame = view.bounds
debugView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
debugView.backgroundColor = UIColor(white: 0.95, alpha: 1)
view.addSubview(debugView)
// Attach a pan recognizer that logs its state
let panGR = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
panGR.allowedScrollTypesMask = .all
debugView.addGestureRecognizer(panGR)
}
@objc private func handlePan(_ gr: UIPanGestureRecognizer) {
switch gr.state {
case .possible:
print("Pan state: possible")
case .began:
print("Pan state: began")
case .changed:
print("Pan state: changed – translation = \(gr.translation(in: debugView))")
case .ended:
print("Pan state: ended – velocity = \(gr.velocity(in: debugView))")
case .cancelled:
print("Pan state: cancelled")
case .failed:
print("Pan state: failed")
@unknown default:
print("Pan state: unknown")
}
}
}
class DebugView: UIView {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
for t in touches {
let typeDesc: String
switch t.type {
case .direct: typeDesc = "direct (finger)"
case .indirectPointer: typeDesc = "indirectPointer (trackpad/mouse)"
case .indirect: typeDesc = "indirect (Apple TV remote)"
case .pencil: typeDesc = "pencil (Apple Pencil)"
@unknown default: typeDesc = "unknown"
}
print("touchesBegan on DebugView – touch type: \(typeDesc), location: \(t.location(in: self))")
}
}
}
Hello 👋🏽
I am a new iOS developer and I am having a hard time understanding the behavior of the new UIPasteControl to avoid showing the system prompt.
Does anyone has any example code I could look at to understand the behavior. Idk how to set the target of the button to the general pasteboard.
also I am using objective-c .
thanks
cristian
I've read in this post that in order to make the configuration of UIButton adjust my custom font's size automatically I need to add implementation to recalculate the font's size inside configurationUpdateHandler. But how would this look like?
I also read something about matching the font's text style. But at this point I'm just guessing. Here's the code:
let loginButton = UIButton(configuration: config, primaryAction: nil)
loginButton.configurationUpdateHandler = { button in
guard var config = button.configuration else { return }
let traits = button.traitCollection
let baseTitleFont = UIFont.customFont(ofSize: 18, weight: .semibold)
let baseSubtitleFont = UIFont.customFont(ofSize: 18, weight: .regular)
let scaledTitleFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: baseTitleFont, compatibleWith: traits)
let scaledSubtitleFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: baseSubtitleFont, compatibleWith: traits)
config.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in
var outgoing = incoming
outgoing.font = scaledTitleFont
return outgoing
}
config.subtitleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in
var outgoing = incoming
outgoing.font = baseSubtitleFont
return outgoing
}
button.configuration = config
}
Thanks in advance!
Looking for a solution that keeps the presented view controller fullscreen.
The background is that I need to take some screenshots of the presenting view controller while the modal is onscreen. It's a pretty messy operation and we don't want to distract and delay the user by doing it before the modal is presented.
Topic:
UI Frameworks
SubTopic:
UIKit
My app crashed on iOS 18.1 only, here is the crash log:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'BUG IN CLIENT OF UIKIT: <CPTemplateApplicationScene: 0x1114bb780> -[UIScene _registerSettingsDiffActionArray:forKey:]: Registering the scene itself results in a retain cycle.'
And I found this post: https://mastodon.social/@marcoarment/113280078320422999
Is this a system bug in iOS 18.1 beta 5?
I am adapting my custom UI Framework for visionOS, and I'm wondering if it is going to be possible to detect hover over different UI elements within my view. The UI Framework draws to a Metal layer in a UIView. I don't currently support uihovergesturerecognizer on the view but I guess this wouldn't help, since you don't get coordinates. I can imagine an unpleasant solution might be to add invisible UIControls for each of my custom controls that are drawn in my own framework.
I've added some menu actions to toggle various text view attributes named in the subject. The default for Simulator and my devices is to have these features turned on. But I'm finding that toggling them in my menu actions doesn't actually work.
Toggling spellchecking or smart quotes (I haven't yet bothered to add more actions and test them) to .off does indeed set the correct value on the UITextView, but the features are still happening when I type (soft or hard keyboards behave the same).
What's wrong? Is it simply broken and is caching the initial value or something? 18.4 is being used on my Simulator and my devices. I also tried 18.3.1 in Simulator with the same results.
Topic:
UI Frameworks
SubTopic:
UIKit
I am experiencing memory leaks in my iOS app that seem to be related to an issue between UIInputView and _UIInputViewContent. After using the memory graph, I'm seeing that instances of these objects aren't being deallocated properly.
The UIInputViewController whichs holds the inputView is being deallocated properly along with its subviews.I have tried to remove all of UIInputViewController's subviews and their functions but the uiInputView is not being deallocated.
The current setup of my app is a collectionView with multiple cells,each possessing a textfield with holds a UIInputViewController.When i scroll up or down,the views are being reused as expected and the number of UIInputViewController stays consistent with the number of textfields.However the number of inputView keeps increasing referencing solely _UIInputViewContent.
class KeyboardViewController: UIInputViewController {
// Callbacks
var key1: ((String) -> Void)?
var key2: (() -> Void)?
var key3: (() -> Void)?
var key4: (() -> Void)?
private lazy var buttonTitles = [
["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"]
]
override func viewDidLoad() {
super.viewDidLoad()
setupKeyboard()
}
lazy var mainStackView: UIStackView = {
let mainStackView = UIStackView()
mainStackView.axis = .vertical
mainStackView.distribution = .fillEqually
mainStackView.spacing = 16
mainStackView.translatesAutoresizingMaskIntoConstraints = false
return mainStackView
}()
private func setupKeyboard() {
let keyboardView = UIView(frame:CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 279.0))
keyboardView.addSubview(mainStackView)
NSLayoutConstraint.activate([
mainStackView.topAnchor.constraint(equalTo: keyboardView.topAnchor, constant: 16),
mainStackView.leadingAnchor.constraint(equalTo: keyboardView.leadingAnchor, constant: 0),
mainStackView.trailingAnchor.constraint(equalTo: keyboardView.trailingAnchor, constant: -24),
mainStackView.bottomAnchor.constraint(equalTo: keyboardView.bottomAnchor, constant: -35)
])
// Create rows
for (_, _) in buttonTitles.enumerated() {
let rowStackView = UIStackView()
rowStackView.axis = .horizontal
rowStackView.distribution = .fillEqually
rowStackView.spacing = 1
// Create buttons for each row
for title in rowTitles {
let button = createButton(title: title)
rowStackView.addArrangedSubview(button)
}
mainStackView.addArrangedSubview(rowStackView)
}
self.view = keyboardView
}
private func createButton(title: String) -> UIButton {
switch title {
///returns a uibutton based on title
}
}
// MARK: - Button Actions
@objc private func numberTapped(_ sender: UIButton) {
if let number = sender.title(for: .normal) {
key1?(number)
}
}
@objc private func key2Called() {
key2?()
}
@objc private func key3Called() {
key3?()
}
@objc private func key4Called() {
key4?()
}
deinit {
// Clear any strong references
key1 = nil
key2 = nil
key3 = nil
key4 = nil
for subview in mainStackView.arrangedSubviews {
if let stackView = subview as? UIStackView {
for button in stackView.arrangedSubviews {
(button as? UIButton)?.removeTarget(self, action: nil, for: .allEvents)
}
}
}
mainStackView.removeFromSuperview()
}
}
Environment
iOS 16.3
Xcode 18.3.1
Any insights would be greatly appreciated as this is causing noticeable memory growth in my app over time.
I am experiencing memory leaks in my iOS app that seem to be related to an issue between UIInputView and _UIInputViewContent. After using the memory graph, I'm seeing that instances of these objects aren't being deallocated properly.
The UIInputViewController whichs holds the inputView is being deallocated properly along with its subviews.I have tried to remove all of UIInputViewController's subviews and their functions but the uiInputView is not being deallocated.
The current setup of my app is a collectionView with multiple cell,each possessing a textfield with holds a UIInputViewController.When i scroll up or down,the views are being reused as expected and the number of UIInputViewController stays consistent with the number of textfields.However the number of inputView keeps increasing referencing solely _UIInputViewContent.
class KeyboardViewController: UIInputViewController {
// Callbacks
var key1: ((String) -> Void)?
var key2: (() -> Void)?
var key3: (() -> Void)?
var key4: (() -> Void)?
private lazy var buttonTitles = [
["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"]
]
override func viewDidLoad() {
super.viewDidLoad()
setupKeyboard()
}
lazy var mainStackView: UIStackView = {
let mainStackView = UIStackView()
mainStackView.axis = .vertical
mainStackView.distribution = .fillEqually
mainStackView.spacing = 16
mainStackView.translatesAutoresizingMaskIntoConstraints = false
return mainStackView
}()
private func setupKeyboard() {
let keyboardView = UIView(frame:CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 279.0))
keyboardView.addSubview(mainStackView)
NSLayoutConstraint.activate([
mainStackView.topAnchor.constraint(equalTo: keyboardView.topAnchor, constant: 16),
mainStackView.leadingAnchor.constraint(equalTo: keyboardView.leadingAnchor, constant: 0),
mainStackView.trailingAnchor.constraint(equalTo: keyboardView.trailingAnchor, constant: -24),
mainStackView.bottomAnchor.constraint(equalTo: keyboardView.bottomAnchor, constant: -35)
])
// Create rows
for (_, _) in buttonTitles.enumerated() {
let rowStackView = UIStackView()
rowStackView.axis = .horizontal
rowStackView.distribution = .fillEqually
rowStackView.spacing = 1
// Create buttons for each row
for title in rowTitles {
let button = createButton(title: title)
rowStackView.addArrangedSubview(button)
}
mainStackView.addArrangedSubview(rowStackView)
}
self.view = keyboardView
}
private func createButton(title: String) -> UIButton {
switch title {
///returns a uibutton based on title
}
}
// MARK: - Button Actions
@objc private func numberTapped(_ sender: UIButton) {
if let number = sender.title(for: .normal) {
key1?(number)
}
}
@objc private func key2Called() {
key2?()
}
@objc private func key3Called() {
key3?()
}
@objc private func key4Called() {
key4?()
}
deinit {
// Clear any strong references
key1 = nil
key2 = nil
key3 = nil
key4 = nil
for subview in mainStackView.arrangedSubviews {
if let stackView = subview as? UIStackView {
for button in stackView.arrangedSubviews {
(button as? UIButton)?.removeTarget(self, action: nil, for: .allEvents)
}
}
}
mainStackView.removeFromSuperview()
}
}
Environment
iOS 16.3
Xcode 18.3.1
Any insights would be greatly appreciated as this is causing noticeable memory growth in my app over time.
I have a scrollable text field where the text changes programmatically.
when I scroll up to see the end of the text, I can keep scrolling so that all of the text scrolls out of site (this is bad). I should only be able to scroll up until I reach the end of the text.
Even worse, something in the scrolling goes into an infinite loop, and the app starts use up memory until I have to kill it.
I assume that it is something that is set wrong in Main.storyboard, but I am not sure.
Andy ideas?
Topic:
UI Frameworks
SubTopic:
UIKit
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?
Hi,
In a Mac Catalyst app, I need to allow the user insert a passcode using a UITextField.
The field is used to insert a one time passcode and I want to keep the content hidden. For this reason I set the isSecureTextEntry property to true.
passcodeTextField.isSecureTextEntry = true
By doing this, a button to allow the user to pick a password from the keychain is displayed:
This option in my case should not appear because the password is a one time password that change every time. For that reason I set the textContentType to oneTimeCode.
passcodeTextField.textContentType = .oneTimeCode
This actually removes the password button, but introduce something weird. If the user type something and then delete everything, a big empty box appear under the field:
I have no idea what this box is and why it appears.
Does anyone know why it appears and how I can remove it?
Thank you
I would like to print a NSTextStorage on multiple pages and add annotations to the side margins corresponding to certain text ranges. For example, for all occurrences of # at the start of a line, the side margin should show an automatically increasing number.
My idea was to create a NSLayoutManager and dynamically add NSTextContainer instances to it until all text is laid out. The layoutManager would then allow me to get the bounding rectangle of the interesting text ranges so that I can draw the corresponding numbers at the same height inside the side margin. This approach works well on macOS, but I'm having some issues on iOS.
When running the code below in an iPad Simulator, I would expect that the print preview shows 3 pages, the first with the numbers 0-1, the second with the numbers 2-3, and the last one with the number 4. Instead the first page shows the number 4, the second one the numbers 2-4, and the last one the numbers 0-4. It's as if the pages are inverted, and each page shows the text starting at the correct location but always ending at the end of the complete text (and not the range assigned to the relative textContainer).
I've created FB17026419.
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
let printController = UIPrintInteractionController.shared
let printPageRenderer = PrintPageRenderer()
printPageRenderer.pageSize = CGSize(width: 100, height: 100)
printPageRenderer.textStorage = NSTextStorage(string: (0..<5).map({ "\($0)" }).joined(separator: "\n"), attributes: [.font: UIFont.systemFont(ofSize: 30)])
printController.printPageRenderer = printPageRenderer
printController.present(animated: true) { _, _, error in
if let error = error {
print(error.localizedDescription)
}
}
}
}
class PrintPageRenderer: UIPrintPageRenderer, NSLayoutManagerDelegate {
var pageSize: CGSize!
var textStorage: NSTextStorage!
private let layoutManager = NSLayoutManager()
private var textViews = [UITextView]()
override var numberOfPages: Int {
if !Thread.isMainThread {
return DispatchQueue.main.sync { [self] in
numberOfPages
}
}
printFormatters = nil
layoutManager.delegate = self
textStorage.addLayoutManager(layoutManager)
if textStorage.length > 0 {
let glyphRange = layoutManager.glyphRange(forCharacterRange: NSRange(location: textStorage.length - 1, length: 0), actualCharacterRange: nil)
layoutManager.textContainer(forGlyphAt: glyphRange.location, effectiveRange: nil)
}
var page = 0
for textView in textViews {
let printFormatter = textView.viewPrintFormatter()
addPrintFormatter(printFormatter, startingAtPageAt: page)
page += printFormatter.pageCount
}
return page
}
func layoutManager(_ layoutManager: NSLayoutManager, didCompleteLayoutFor textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) {
if textContainer == nil {
addPage()
}
}
private func addPage() {
let textContainer = NSTextContainer(size: pageSize)
layoutManager.addTextContainer(textContainer)
let textView = UITextView(frame: CGRect(origin: .zero, size: pageSize), textContainer: textContainer)
textViews.append(textView)
}
}
I am a developer on an enterprise application. Our team just updated our pipeline to build our app on the iOS 18 SDK instead of the 17.4 SDK and this has caused a lot of our ui elements to change and several crashes within the app resulting in just the simple error message "Swift runtime failure: unhandled C++ / Objective-C exception".
Why is just updating the SDK causing all these issues? Is there anyway to keep the previous version or will we have to go component by component to fix the constraints and crashes? These issues seem to be happening to our users on iOS 18 and beyond.