I have an app that uses UITextView for some text editing. I have some custom operations I can do on the text that I want to be able to undo, and I'm representing those operations in a way that plugs into NSUndoManager nicely. For example, if I have a button that appends an emoji to the text, it looks something like this:
func addEmoji() {
let inserting = NSAttributedString(string: "😀")
self.textStorage.append(inserting)
let len = inserting.length
let range = NSRange(location: self.textStorage.length - len, length: len)
self.undoManager?.registerUndo(withTarget: self, handler: { view in
view.textStorage.deleteCharacters(in: range)
}
}
My goal is something like this:
Type some text
Press the emoji button to add the emoji
Trigger undo (via gesture or keyboard shortcut) and the emoji is removed
Trigger undo again and the typing from step 1 is reversed
If I just type and then trigger undo, the typing is reversed as you'd expect. And if I just add the emoji and trigger undo, the emoji is removed. But if I do the sequence above, step 3 works but step 4 doesn't. The emoji is removed but the typing isn't reversed.
Notably, if step 3 only changes attributes of the text, like applying a strikethrough to a selection, then the full undo chain works. I can type, apply strikethrough, undo strikethrough, and undo typing.
It's almost as if changing the text invalidates the undo manager's previous operations?
How do I insert my own changes into UITextView's NSUndoManager without invalidating its chain of other operations?
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
I am fighting with a weird problem
in my app I use swiftui and ai do not use tabs, but a hierarchy of view created by me that switch one to the other with a slide animation when I tap one of the elements
i don't use gesture, but only ontap.
The same app launched on two different iPhone has two different behaviours
on the iPhone 11 no problem, the animation is fluid and responsive, on the iPhone 10 I got some delay, and in the console I can read Gesture: System gesture gate timed out
I have searched but I have not found anything useful and more than this it is extremely difficult to find some info about this warning/error
How can I solve this?
on the iPhone 10 it seems like the animation got stuck for 1 seconds prior to be executed in the correct way
I've discovered an issue with using iOS 16's Transferable drag-and-drop APIs for SwiftUI. The dropDestination modifier does not work when applied to a subview of a List.
This code below will not work, unless you replace the List with a VStack or any other container (which, of course, removes all list-specific rendering).
The draggable modifier will still work and the item will drag, but the dropDestination view won't react to it and neither closure will be called.
struct MyView: View {
var body: some View {
List {
Section {
Text("drag this title")
.font(.largeTitle)
.draggable("a title")
}
Section {
Color.pink
.frame(width: 400, height: 400)
.dropDestination(for: String.self) { receivedTitles, location in
true
} isTargeted: {
print($0)
}
}
}
}
}
Has anyone encountered this bug and perhaps found a workaround?
In Monterey, when a user was at the top of a ScrollView implemented inside of a NSHostingController's view (that was itself embedded in a window with a NSToolbar), the window's toolbar background would be hidden until the user scrolled from the top.
In Ventura, this behavior is different, with the toolbar's background visible all of the time unless a traditional NSScrollView is used (which means no SwiftUI).
Is there the ability to change this behavior within SwiftUI some how now?
Hello and thanks for reading my post.
I have a SwiftUI view, the users should be able to click a button and take printout of that view. Clicking on the button should open the standard print sheet (select printer, pages, layout, etc.).
How can I implement such a functionality? I have been trying hard without any success. Please help.
It is an iPad app, using Xcode 14.3
I'm in the process of migrating to the Observation framework but it seems like it is not compatible with didSet. I cannot find information about if this is just not supported or a new approach needs to be implemented?
import Observation
@Observable class MySettings {
var windowSize: CGSize = .zero
var isInFullscreen = false
var scalingMode: ScalingMode = .scaled {
didSet {
...
}
}
...
}
This code triggers this error:
Instance member 'scalingMode' cannot be used on type 'MySettings'; did you mean to use a value of this type instead?
Anyone knows what needs to be done? Thanks!
I am using a LayzVStack embedded into a ScrollView. The list items are fetched from a core data store by using a @FetchResult or I tested it also with the new @Query command coming with SwiftData.
The list has one hundred items 1, 2, 3, ..., 100.
The user scrolled the ScrollView so that items 50, 51, ... 60 are visible on screen.
Now new data will be fetched from the server and updates the CoreData or SwiftData model. When I add new items to the end of the list (e.g 101, 102, 103, ...) then the ScrollView is keeping its position.
Opposite to this when I add new items to the top (0, -1, -2, -3, ...) then the ScrollView scrolls down.
Is there a way with the new SwiftData and SwiftUI ScrollView modifiers to update my list model without scrolling like with UIKit where you can query and set the scroll offset pixel wise?
When I update a variable inside my model that is marked @Transient, my view does not update with this change. Is this normal? If I update a non-transient variable inside the model at the same time that I update the transient one, then both changes are propagated to my view.
Here is an example of the model:
@Model public class WaterData {
public var target: Double = 3000
@Transient public var samples: [HKQuantitySample] = []
}
Updating samples only does not propagate to my view.
Simplely, when we set UITextView.inputView and then call becomeFirstResponder, but the custom inputView could not show expectedly just like before. We test this code in iOS17 and below, while only iOS17 does not work.
And xcode console print these logs:
Failed to retrieve snapshot.
-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID
-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID
-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID
-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID
Unsupported action selector setShiftStatesNeededInDestination:autoShifted:shiftLocked:
Unsupported action selector setShiftStatesNeededInDestination:autoShifted:shiftLocked:
Unsupported action selector setShiftStatesNeededInDestination:autoShifted:shiftLocked:
Unsupported action selector setShiftStatesNeededInDestination:autoShifted:shiftLocked:
So I am trying to move an old project from ios16 to ios17... wanted to play around with the new previews, and animations and first error I get is the above.
When I create a new project I can use the macro just fine.
What is more: when I add a new swiftUI file I get the same error with the #Preview macro.
I went through the project and target settings, making sure everything is set to ios17 and Swift5, but can't find any other settings. Cleared the build cache and rebuilt from scratch.
Hoping someone else ran onto the same problem and found a solution?
Without using #Preview
Is it possible to specify a default window size for a 2D window in visionOS? I know this is normally achieved by modifying the WindowGroup with .defaultSize(width:height:), but I get an error that this was not included in "xrOS". I am able to specify .defaultSize(width:height:depth:) for a volumetric window, but this doesn't have any effect when applied to a 2D one.
Hi. The binding in a ForEach or List view doesn't work anymore when using the @Observable macro to create the observable object. For example, the following are the modifications I introduced to the Apple's example called "Migrating from the Observable Object Protocol to the Observable Macro" https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro
struct LibraryView: View {
@Environment(Library.self) private var library
var body: some View {
List($library.books) { $book in
BookView(book: book)
}
}
}
All I did was to add the $ to turn the reference to library.books into a binding but I got the error "Cannot find '$library' in scope"
Is this a bug or the procedure to use binding in lists changed?
Thanks
On iOS17, UIDevice.current.batteryLevel is returning values rounded to 0.05, such as 1, 0.95, 0.9. Which used to be a 1% granularity in iOS16. Is this a bug or a new feature?
I've tried to animate custom UIViewRepresentable with SwitfUI animations, but it doesn't work. It just sets value without interpolation. What should i do to use interpolation values in UIKit views?
My example shows two "progress bars" red one is UIKit view, blue one is SwiftUI version. Sliders controls value directly, randomize button changes value to random with 5s animation.
When I press button SwiftUI progress bar animates exactly as it should, but UIKit's one just jumps to final position.
Set block of animatableData inside Animatable extension not called.
How can I use SwiftUI animation value interpolations for UIKit?
import SwiftUI
import UIKit
class UIAnimationView: UIView {
var progress: CGFloat = 0.5 {
didSet {
if self.progressConstraint != nil, self.innerView != nil {
self.removeConstraint(self.progressConstraint!)
}
let progressConstraint = NSLayoutConstraint(
item: innerView!,
attribute: .trailing,
relatedBy: .equal,
toItem: self,
attribute: .trailing,
multiplier: min(1.0, max(0.0001, progress)),
constant: 0
)
self.addConstraint(progressConstraint)
self.progressConstraint = progressConstraint
self.layoutIfNeeded()
}
}
var innerView: UIView?
private var progressConstraint: NSLayoutConstraint?
public override init(frame: CGRect) {
super.init(frame: frame)
self.performInit()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
self.performInit()
}
private func performInit() {
let innerView = UIView()
innerView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(innerView)
self.leadingAnchor.constraint(equalTo: innerView.leadingAnchor).isActive = true
self.topAnchor.constraint(equalTo: innerView.topAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: innerView.bottomAnchor).isActive = true
let progressConstraint = NSLayoutConstraint(
item: innerView,
attribute: .trailing,
relatedBy: .equal,
toItem: self,
attribute: .trailing,
multiplier: progress,
constant: 0
)
self.progressConstraint = progressConstraint
self.addConstraint(progressConstraint)
self.innerView = innerView
self.innerView!.backgroundColor = UIColor.red
self.backgroundColor = UIColor.black
}
}
struct AnimationTest: UIViewRepresentable {
var progress: CGFloat
typealias UIViewType = UIAnimationView
func updateUIView(_ uiView: UIAnimationView, context: Context) {
print("progress: \(progress) \(context.transaction.isContinuous)")
uiView.progress = progress
}
func makeUIView(context: Context) -> UIAnimationView {
let view = UIAnimationView()
view.progress = progress
return view
}
}
extension AnimationTest: Animatable {
var animatableData: CGFloat {
get {
return progress
}
set {
print("Animation \(newValue)")
progress = newValue
}
}
}
struct AnimationDebug: View {
@State var progress: CGFloat = 0.75
var body: some View {
VStack {
AnimationTest(progress: progress)
Spacer()
VStack {
Slider(value: $progress, in: 0...1) {
Text("Progress")
}
}
GeometryReader { gr in
Color.blue
.frame(
width: gr.size.width * progress,
height: 48)
}
.frame(height: 48)
Button("Randomize") {
withAnimation(Animation.easeInOut(duration: 5)) {
progress = CGFloat.random(in: 0...1)
}
}
}
}
}
struct AnimationTest_Previews: PreviewProvider {
static var previews: some View {
AnimationDebug()
}
}
Apple docs for RealityView state:
You can also use the optional update closure on your RealityView to update your RealityKit content in response to changes in your view’s state."
Unfortunately, I've not been able to get this to work.
All of my 3D content is programmatically generated - I'm not using any external 3D modeling tools. I have an object that conforms to @ObservableObject. Its @Published variables define the size of a programmatically created Entity. Using the initializer values of these @Published variables, the 1st rendering of the RealityView { content in } works like a charm.
Basic structure is this:
var body: some View {
RealityView { content in
// create original 3D content using initial values of @Published variables - works perfect
} update: { content in
// modify 3D content in response to changes of @Published variables - never works
}
Debug statements show that the update: closure gets called as expected - based upon changes in the viewModel's @Published variables. However, the 3D content never changes - even though the 3D content is based upon the @Published variables.
Obviously, if the @Published variables are used in the 1st rendering, and the update: closure is called whenever changes occur to these @Published variables, then why isn't the update: closure updating the RealityKit content as described in the Apple docs?
I've tried everything I can think of - including removing all objects in the update: closure and replacing them with the same call that populated them in the 1st rendering. Debug statements show that the new @Published values are correct as expected when the update: closure is called, but the RealityView never changes.
Known limitation of the beta? Programmer error? Thoughts?
Help,I have encountered a thorny problem! In systems with iOS 16.5 and above, there is a probability that the keyboard will not disappear after it appears. And once it appears, unless the app is restarted, all places where the keyboard is used cannot be closed.
I have tried using the forced shutdown method [UIView endEditing:YES], but it didn't work. When this exception occurs, I notice that there will be two UITextEffectsWindow at the same time.
Does anyone know how to solve it?
I am working on creating a file viewer to browse a network directory as a way to introduce myself to iOS app development, and was trying to implement a feature that would allow users to drag and drop both files and folders onto another folder inside of the app to move items around. However, it seems that if the View that is set to draggable, and then the view that is set as the Drop Destination is in the same List, then the Drop Destination will not detect when the draggable view has been dropped onto it. Here is the structure of my code:
List {
Section(header: Text("Folders")) {
ForEach($folders, id: \.id) { $folder in
FolderCardView()
.onDrop(of: [UTType.item], isTargeted: $fileDropTargeted, perform: { (folders, cgPoint) -> Bool in
print("Dropped")
return true
})
}
}
Section(header: Text("Files")) {
ForEach($files, id: \.id) { $file in
FileCardView()
.onDrag({
let folderProvider = NSItemProvider(object: file)
return folderProvider
})
}
}
}
I have verified that the issue comes down to the list, because if I move both of the ForEach loops out on their own, or even into their own respective lists, the code works perfectly. I have not only tested this with the older .onDrop and .onDrag modifiers as shown above, but also the newer .draggable and .dropDestination modifiers, and the result is the same.
Does anyone know if this is intended behavior? I really like the default styling that the List element applies to other elements within, so I am hoping that it might just be a bug or an oversight. Thanks!
Hi,
I'm trying to build iOS app, but I found out that .onPreferenceChange has strange behaviour if the view contains an if statement below view which sets .preference.
Here is an repository with minimal reproduction: https://github.com/Mordred/swiftui-preference-key-bug
There should be displayed title text on the top and bottom of the screen. But the bottom is empty.
If you delete if statement if true { at https://github.com/Mordred/swiftui-preference-key-bug/blob/main/PreferenceKeyBug/PreferenceKeyBug.swift then it works fine.
Please update charts framework to have z axis for 3D plotting
Hi,
In the visionOS documentation
Positioning and sizing windows - Specify initial window position
In visionOS, the system places new windows directly in front of people, where they happen to be gazing at the moment the window opens.
Positioning and sizing windows - Specify window resizability
In visionOS, the system enforces a standard minimum and maximum size for all windows, regardless of the content they contain.
The first thing I don't understand is why it talk about macOS in visionOS documentation.
The second thing, what is this page for if it's just to tell us that on visionOS we have no control over the position and size of 2D windows. Whereas it is precisely the opposite that would be interesting. I don't understand this limitation. It limits so much the use of 2D windows under visionOS.
I really hope that this limitation will disappear in future betas.