Post

Replies

Boosts

Views

Activity

Specifying Item Spacing in UICollectionLayoutListConfiguration
Currently, I am using UICollectionViewCompositionalLayout to achieve the following list layout with spacing: We were thinking of trying out UICollectionLayoutListConfiguration due to its ability to support swipe actions. We would like to specify the spacing between each item. However, I do not find a way to specify item spacing in UICollectionLayoutListConfiguration. Does UICollectionLayoutListConfiguration not support item spacing, or have I missed something? Thanks.
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
911
Jul ’24
Customizing Drop Location View in UICollectionView Drag and Drop
Currently, this is how I implement the drag and move operation: collectionView.beginInteractiveMovementForItem collectionView.updateInteractiveMovementTargetPosition collectionView.endInteractiveMovement The outcome looks like the following: However, what I would like to achieve is the ability to customize the view of the "drop" location. For instance, in the following example, a red line is drawn at the target drop location: In this example, a transparent rectangle is drawn at the target drop location: May I know how these apps achieve such an effect? Thanks.
Topic: UI Frameworks SubTopic: UIKit Tags:
0
0
443
Jul ’24
How to deal with Guideline 2.1 - Information Needed (App Tracking Transparency framework related)
My app, which only shows ads one day after it is first launched, keeps being rejected due to Guideline 2.1 - Information Needed (App Tracking Transparency framework related). Here is the rejection message from Apple: Guideline 2.1 - Information Needed We're looking forward to completing our review, but we need more information to continue. Your app uses the AppTrackingTransparency framework, but we are unable to locate the App Tracking Transparency permission request when reviewed on iOS 17.5.1. Next Steps Please explain where we can find the App Tracking Transparency permission request in your app. The request should appear before any data is collected that could be used to track the user. If you've implemented App Tracking Transparency but the permission request is not appearing on devices running the latest operating system, please review the available documentation and confirm App Tracking Transparency has been correctly implemented. If your app does not track users, update your app privacy information in App Store Connect to not declare tracking. You must have the Account Holder or Admin role to update app privacy information. We have provided a detailed step-by-step guide to trigger the App Tracking Transparency. We also attached videos demonstrating how to adjust the date to one day ahead in order to show the ads. Here is our reply: Dear Reviewer, XXX will start displaying ads one day after the app is first launched. To see the App Tracking Transparency permission request and subsequent ads, please follow these steps: 1. Launch XXX for the first time. 2. Close and completely exit the XXX app. 3. Adjust the date on your device to one day ahead. 4. Re-launch XXX. You will see the App Tracking Transparency permission request. For a visual guide, please refer to the attached videos: https://youtube.com/shorts/07qsYuFkPBY - Demonstrates when the user allows tracking. https://youtube.com/shorts/voQfgjAN7h0 - Demonstrates when the user does not allow tracking. If you need further explanation, please contact me at +xxxx-xxxxxxx. Thank you. P.S. I frequently receive similar inquiries with each submission of my app. Could you please add a note to my app to prevent this recurring issue in future reviews? Thank you for your attention. However, Apple continues to reject our update, despite our clear instructions for showing App Tracking Transparency. Do you have any suggestions on what we can do next? Thank you.
2
0
1.5k
Jul ’24
Implementing Undo/Redo Feature in UITextView with IME Support
Currently, we are implementing an undo/redo feature in UITextView. However, we cannot use the built-in UndoManager in UITextView because we have multiple UITextView instances inside a UICollectionView. Since UICollectionView recycles UITextView instances, the same UITextView might be reused in different rows, making the built-in UndoManager unreliable. The shouldChangeTextIn method in UITextViewDelegate is key to implementing undo/redo functionality properly. Here is an example of our implementation: extension ChecklistCell: UITextViewDelegate { func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { // Get the current text let s = textView.text ?? "" // Get the starting position of the change let start = range.location // Get the number of characters that will be replaced let count = range.length // Get the number of characters that will be added let after = text.count print(">>>> The current text = \"\(s)\"") print(">>>> The starting position of the change = \(start)") print(">>>> The number of characters that will be replaced = \(count)") print(">>>> The number of characters that will be added = \(after)") print(">>>>") if let delegate = delegate, let checklistId = checklistId, let index = delegate.checklistIdToIndex(checklistId) { delegate.attachTextAction(s: s, start: start, count: count, after: after, index: index) } return true } } Working scene behind the UITextViewDelegate However, this implementation does not work well with non-English input using an IME. When using an IME, there is an intermediate input before the final input is produced. For example, typing "wo" (intermediate input) produces "我" (final input). Currently, UITextViewDelegate captures both "wo" and "我". UITextViewDelegate captures both "wo" and "我" Is there a way to ignore the intermediate input from IME and only consider the final input? In Android, we use the beforeTextChanged method in TextWatcher to seamlessly ignore the intermediate input from IME and only consider the final input. You can see this in action in this Android captures only "我" Is there an equivalent way in iOS to ignore the intermediate input from IME and only take the final input into consideration?
Topic: UI Frameworks SubTopic: UIKit Tags:
0
0
580
Jun ’24
In-App Purchase Review Delaying App Release - Next Steps?
Our submitted binary app version has been approved by Apple reviewers. It's currently pending Developer Release. However, our in-app purchase, which was submitted at the same time, is still under review. Since the newly added in-app purchase hasn't been approved yet, we've decided to hold off on releasing the binary app version. Should we just wait for Apple to approve the in-app purchase, or are there any other recommended actions? Thank you.
2
0
813
Jun ’24
Not able login to simulator using the newly created sandbox account
I wish to login simulator using a sandbox account so that I can test on in-app purchase and subscription. I have created a new apple id account using a new email. I have confirmed the new apple id + password is correct, by login into https://appleid.apple.com/#!&page=signin I also added the new apple id account into App Store Connect as sandbox tester - However, I am still not able login to simulator. I am keep getting error - "Username or password is incorrect" You can see in the simulator background. I can login to https://appleid.apple.com/#!&page=signin using the new apple id + password via web browser. But, I am not sure why I am not able login into the simulator itself. Can anyone assist me on this? Thank you.
2
4
2.2k
Mar ’24
UICollectionView + UISearchController - How to achieve smooth scrolling animation when hiding search bar?
I notice that, normal implementation for UICollectionView + UISearchController, will not able to achieve smooth scrolling animation when hiding search bar. As you can see in the below video, when we scroll upward the page, it seems like "the page has slipped up suddenly". If we compare the animation with search bar in iOS Settings page, the problem is more obvious. iOS Settings page able to have a smooth scrolling experience. Implementation The following is our implementation. The complete workable project is found at : https://github.com/yccheok/demo-uicollectionview-uisearchcontroller class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate { @IBOutlet weak var collectionView: UICollectionView! let reuseIdentifier = "cell" // also enter this string as the cell identifier in the storyboard var items = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "bye"] private lazy var searchController: UISearchController = { let searchController = UISearchController(searchResultsController: UIViewController()) searchController.searchResultsUpdater = self searchController.obscuresBackgroundDuringPresentation = true searchController.searchBar.placeholder = "search_todos" searchController.searchBar.delegate = self return searchController }() override func viewDidLoad() { super.viewDidLoad() collectionView.dataSource = self collectionView.delegate = self navigationItem.searchController = searchController } // MARK: - UICollectionViewDataSource protocol // tell the collection view how many cells to make func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.items.count } // make a cell for each cell index path func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // get a reference to our storyboard cell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MyCollectionViewCell // Use the outlet in our custom class to get a reference to the UILabel in the cell cell.label.text = self.items[indexPath.item] cell.backgroundColor = .yellow return cell } // MARK: - UICollectionViewDelegate protocol func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { // handle tap events print("You selected cell #\(indexPath.item)!") } } class MyCollectionViewCell: UICollectionViewCell { @IBOutlet weak var label: UILabel! } extension ViewController: UISearchResultsUpdating { func updateSearchResults(for searchController: UISearchController) { } } extension ViewController: UISearchBarDelegate { } Do you have any idea, how we can achieve such a smooth scrolling animation? Thanks.
Topic: UI Frameworks SubTopic: UIKit Tags:
0
1
896
Nov ’23
WidgetKit iOS 17 : Interactive widget no longer responsive after a long idle time
I have developed an interactive widget which looks as the following It is using CoreData. The view is implemented as the following struct widget_extensionEntryView : View { var body: some View { if let nsTodoList = nsTodoList { VStack(alignment: .leading, spacing: 1) { ... ForEach(0..<prefixGoodNSTodoArray.count, id: \.self) { index in ... let todoIntent = TodoIntent(objectIDAsString: objectIDAsString, parentOrder: parentOrder) Button(intent: todoIntent) { } } } } } } The AppIntent is implemented as the following import Foundation import AppIntents import WidgetKit import CoreData struct TodoIntent: AppIntent { static var title: LocalizedStringResource = "Complete Task" static var description: IntentDescription = IntentDescription("Complete selected task") @Parameter(title: "objectIDAsString") var objectIDAsString: String @Parameter(title: "parentOrder") var parentOrder: Int init() { } init(objectIDAsString: String, parentOrder: Int) { self.objectIDAsString = objectIDAsString self.parentOrder = parentOrder } func perform() async throws -> some IntentResult { guard let objectID = NSManagedObjectID.from(objectIDAsString) else { return .result() } guard let nsTodoList = NSTodoListRepository.INSTANCE.getNSTodoList(objectID) else { return .result() } nsTodoList.toggleChecked(context: CoreDataStack.INSTANCE.viewContext, parentOrder: Int64(parentOrder)) RepositoryUtils.saveContextIfPossible(CoreDataStack.INSTANCE.viewContext) TodoWidgetOptions.isWrittenByWidgetExtension = true // Refresh all home widgets. // TODO: https://developer.apple.com/forums/thread/721937 WidgetCenter.shared.reloadAllTimelines() return .result() } } The interactive widget works pretty well. However, tapping on the widget has no response in the following situations: After an overnight, we turn on the iPhone's screen and tap on the widget. After a few hours of idle time, we turn on the iPhone's screen and tap on the widget. One of the steps below will make the widget workable again: Launch and close the main app again. The main app will call WidgetCenter.shared.reloadAllTimelines() during sceneDidEnterBackground. Press and hold on the widget, choose 'Edit widget', and select the desired Todo list. One thing to take note of is that I am using IntentTimelineProvider instead of AppIntentTimelineProvider. The reason I am using 'older tech' is due to the limitation mentioned in https://developer.apple.com/forums/thread/741053 However, I am not sure whether this is the root cause of the problem. Does anyone have any idea why such a problem occurs? Thanks.
2
1
1.5k
Nov ’23
In iOS 17 AppIntent, how we can perform dynamic data selection for Widget?
Before iOS17, when we implement "Edit widget" feature with dynamic data, we are using the following ways. Using an intent definition file Using an intent extension Here's the outcome. A searchable view controller Multi sectioned data view controller Here is the implementation intent definition file + intent extension class IntentHandler: INExtension, ConfigurationIntentHandling { func provideStickyNoteWidgetItemOptionsCollection(for intent: ConfigurationIntent, with completion: @escaping (INObjectCollection<StickyNoteWidgetItem>?, Error?) -> Void) { var stickyNoteWidgetItems = [StickyNoteWidgetItem]() var archivedStickyNoteWidgetItems = [StickyNoteWidgetItem]() let allStickyNoteWidgetItems = NSPlainNoteRepository.getStickyNoteWidgetItemsWithoutTrash() for allStickyNoteWidgetItem in allStickyNoteWidgetItems { if allStickyNoteWidgetItem.archived == 1 { archivedStickyNoteWidgetItems.append(allStickyNoteWidgetItem) } else { stickyNoteWidgetItems.append(allStickyNoteWidgetItem) } } var sections = [INObjectSection<StickyNoteWidgetItem>]() if !stickyNoteWidgetItems.isEmpty { let section = INObjectSection(title: nil, items: stickyNoteWidgetItems) sections.append(section) } if !archivedStickyNoteWidgetItems.isEmpty { let archivedSection = INObjectSection(title: "archive".localized, items: archivedStickyNoteWidgetItems) sections.append(archivedSection) } let collection = INObjectCollection(sections: sections) completion(collection, nil) } override func handler(for intent: INIntent) -> Any { // This is the default implementation. If you want different objects to handle different intents, // you can override this and return the handler you want for that particular intent. return self } } However, if I were using AppIntent in iOS17, I can only achieve the following Not searchable. Not section-able. Using AppIntent import Foundation import AppIntents @available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) struct StickyNoteWidgetItemAppEntity: AppEntity { static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "StickyNoteWidgetItem") @Property(title: "archived") var archived: Bool? struct StickyNoteWidgetItemAppEntityQuery: EntityQuery { func entities(for identifiers: [StickyNoteWidgetItemAppEntity.ID]) async throws -> [StickyNoteWidgetItemAppEntity] { // TODO: return StickyNoteWidgetItemAppEntity entities with the specified identifiers here. return [] } func suggestedEntities() async throws -> [StickyNoteWidgetItemAppEntity] { // TODO: return likely StickyNoteWidgetItemAppEntity entities here. // This method is optional; the default implementation returns an empty array. return [ StickyNoteWidgetItemAppEntity(id: "id0", displayString: "note 0"), StickyNoteWidgetItemAppEntity(id: "id1", displayString: "note 1"), StickyNoteWidgetItemAppEntity(id: "id2", displayString: "note 2") ] } } static var defaultQuery = StickyNoteWidgetItemAppEntityQuery() var id: String // if your identifier is not a String, conform the entity to EntityIdentifierConvertible. var displayString: String var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(displayString)") } init(id: String, displayString: String) { self.id = id self.displayString = displayString } } By using AppIntent, I would like to achieve what is previously achievable using intent defination file + intent extension Is that ever possible? Thanks.
0
2
1.1k
Nov ’23
UISheetPresentationController: Remove Dim Background but Keep Touch-to-Dismiss?
I'm using the UISheetPresentationController to present a view, as shown in the following code: @IBAction func click(_ sender: Any) { let whiteViewController = WhiteViewController.instanceFromMainStoryBoard() if let sheet = whiteViewController.sheetPresentationController { sheet.detents = [.medium()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false sheet.prefersGrabberVisible = false // Remove dim effect. ////sheet.largestUndimmedDetentIdentifier = .medium } self.present(whiteViewController, animated: true) } The outcome is as follow This code results in a presentation with: A dimmed background. A 'Touch anywhere to dismiss' behavior. However, I want to remove the dim background while keeping the 'Touch anywhere to dismiss' functionality. When I add the line: sheet.largestUndimmedDetentIdentifier = .medium The dim background is indeed removed, but the touch-to-dismiss behavior is gone too, as illustrated: Is there a way to achieve both – removing the dim background and retaining the touch-to-dismiss functionality? Thank you.
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
2.2k
Oct ’23
What kind of configuration is required, so that WKWebView able to render image files in AppGroup directory?
I have the following HTML string. We want to render image from our app AppGroup. <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <title>This is title</title> </head> <body> <p><h1>This is title</h1></p> <div style="font-size: 0"> <img src="file:///Users/xxx/Library/Developer/CoreSimulator/Devices/A7B89802-9C65-4512-85A7-51C4372172D0/data/Containers/Shared/AppGroup/14DA3695-BFAF-4096-9F54-2874FD8285C2/attachment/b16c714e-9bb5-4eaa-924e-e043a69088ea.jpeg" width="100%"> </div> This is body text </body> </html> However, if we execute the following code let html = ... // wkWebView is WKWebView wkWebView.loadHTMLString(html, baseURL: nil) Only text is rendered. The image is not rendered. May I know, what kind of configuration is required, so that WKWebView able to render image files in AppGroup directory? Thanks.
Topic: Safari & Web SubTopic: General Tags:
0
0
736
Apr ’23
Throw NSRangeException from NSFetchedResultsController.performFetch after introducing UICollectionViewDiffableDataSource
I was able to avoid NSInternalInconsistencyException by using NSDiffableDatasource with NSFetchedResultsController My data source definition is private typealias DataSource = UICollectionViewDiffableDataSource<Int, NSManagedObjectID> (Does anyone know why we should use Int as SectionIdentifierType? I find it works fine too, if I am using String as SectionIdentifierType) Even though I do not see NSInternalInconsistencyException anymore, I have the following crash log from Firebase Crashlytics. Do you know how to fix that? Fatal Exception: NSRangeException 0 CoreFoundation 0x99288 __exceptionPreprocess 1 libobjc.A.dylib 0x16744 objc_exception_throw 2 CoreFoundation 0x1a4318 -[__NSCFString characterAtIndex:].cold.1 3 CoreFoundation 0x928c8 -[NSArray subarrayWithRange:] 4 CoreData 0x702dc -[_PFArray subarrayWithRange:] 5 CoreData 0xc2d58 -[_NSDefaultSectionInfo objects] 6 CoreData 0x14eb98 -[NSFetchedResultsController _conditionallyDispatchSnapshotToDelegate:updatesInfo:] 7 CoreData 0xc3edc -[NSFetchedResultsController performFetch:] My NSFetchedResultsController look as following private lazy var fetchedResultsController: NSFetchedResultsController<NSPlainNote> = { let fetchRequest: NSFetchRequest<NSPlainNote> = NSPlainNote.fetchRequest( label: label, propertiesToFetch: ["pinned"] ) let controller = NSFetchedResultsController( fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.INSTANCE.viewContext, sectionNameKeyPath: "pinned", cacheName: nil ) controller.delegate = fetchedResultsControllerDelegate return controller }() My NSPlainNote.fetchResult looks as following static func fetchRequest(label: String?, propertiesToFetch: [Any]?) -> NSFetchRequest<NSPlainNote> { let fetchRequest = NSPlainNote.fetchRequest() fetchRequest.propertiesToFetch = propertiesToFetch fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "pinned", ascending: false), NSSortDescriptor(key: "order", ascending: true) ] if let label = label { let predicate = NSPredicate(format: "archived = false AND trashed = false AND label = %@", label) fetchRequest.predicate = predicate } else { let predicate = NSPredicate(format: "archived = false AND trashed = false") fetchRequest.predicate = predicate } return fetchRequest } I am not able to reproduce the problem. However, we can observe crash happens during NSFetchedResultsController.performFetch. For those who are experience in this, do you know what is the root cause, and how I can resolve such? Thanks.
0
0
921
Mar ’23
Does anyone know how I can reset/ cancel my in-app purchase status from TestFlight account?
I install and test my own app, in real device, from TestFlight. I want to able to keep testing on the in-app purchase/ subscription feature. I notice once I have purchase an item success (I was prompted to enter password during purchase), it will forever be marked as purchased. In simulator, if I uninstall then re-install again the app, the in-app purchase status will all be reset. However, for TestFlight in real device, I do not find a way to reset my purchase. (So that I can keep testing on the purchase process) Does anyone know, how I can reset/ cancel my in-app purchase status from TestFlight account?
1
1
996
Mar ’23
Providing in-note text search (UIFindInteraction) feature in iOS 15
I would like to implement in-note text search feature, as found in Apple's Notes, Apple's Safari app. It looks like the following I understand that such an API is called UIFindInteraction, and only available in iOS16. https://developer.apple.com/documentation/uikit/uifindinteraction WWDC 2022: Adopt desktop-class editing interactions However, my app is targeting iOS 15. I was wondering, is it possible for us to provide same feature, in iOS 15? Thank you.
0
0
974
Feb ’23
Specifying Item Spacing in UICollectionLayoutListConfiguration
Currently, I am using UICollectionViewCompositionalLayout to achieve the following list layout with spacing: We were thinking of trying out UICollectionLayoutListConfiguration due to its ability to support swipe actions. We would like to specify the spacing between each item. However, I do not find a way to specify item spacing in UICollectionLayoutListConfiguration. Does UICollectionLayoutListConfiguration not support item spacing, or have I missed something? Thanks.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
1
Boosts
0
Views
911
Activity
Jul ’24
Customizing Drop Location View in UICollectionView Drag and Drop
Currently, this is how I implement the drag and move operation: collectionView.beginInteractiveMovementForItem collectionView.updateInteractiveMovementTargetPosition collectionView.endInteractiveMovement The outcome looks like the following: However, what I would like to achieve is the ability to customize the view of the "drop" location. For instance, in the following example, a red line is drawn at the target drop location: In this example, a transparent rectangle is drawn at the target drop location: May I know how these apps achieve such an effect? Thanks.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
0
Boosts
0
Views
443
Activity
Jul ’24
How to deal with Guideline 2.1 - Information Needed (App Tracking Transparency framework related)
My app, which only shows ads one day after it is first launched, keeps being rejected due to Guideline 2.1 - Information Needed (App Tracking Transparency framework related). Here is the rejection message from Apple: Guideline 2.1 - Information Needed We're looking forward to completing our review, but we need more information to continue. Your app uses the AppTrackingTransparency framework, but we are unable to locate the App Tracking Transparency permission request when reviewed on iOS 17.5.1. Next Steps Please explain where we can find the App Tracking Transparency permission request in your app. The request should appear before any data is collected that could be used to track the user. If you've implemented App Tracking Transparency but the permission request is not appearing on devices running the latest operating system, please review the available documentation and confirm App Tracking Transparency has been correctly implemented. If your app does not track users, update your app privacy information in App Store Connect to not declare tracking. You must have the Account Holder or Admin role to update app privacy information. We have provided a detailed step-by-step guide to trigger the App Tracking Transparency. We also attached videos demonstrating how to adjust the date to one day ahead in order to show the ads. Here is our reply: Dear Reviewer, XXX will start displaying ads one day after the app is first launched. To see the App Tracking Transparency permission request and subsequent ads, please follow these steps: 1. Launch XXX for the first time. 2. Close and completely exit the XXX app. 3. Adjust the date on your device to one day ahead. 4. Re-launch XXX. You will see the App Tracking Transparency permission request. For a visual guide, please refer to the attached videos: https://youtube.com/shorts/07qsYuFkPBY - Demonstrates when the user allows tracking. https://youtube.com/shorts/voQfgjAN7h0 - Demonstrates when the user does not allow tracking. If you need further explanation, please contact me at +xxxx-xxxxxxx. Thank you. P.S. I frequently receive similar inquiries with each submission of my app. Could you please add a note to my app to prevent this recurring issue in future reviews? Thank you for your attention. However, Apple continues to reject our update, despite our clear instructions for showing App Tracking Transparency. Do you have any suggestions on what we can do next? Thank you.
Replies
2
Boosts
0
Views
1.5k
Activity
Jul ’24
Implementing Undo/Redo Feature in UITextView with IME Support
Currently, we are implementing an undo/redo feature in UITextView. However, we cannot use the built-in UndoManager in UITextView because we have multiple UITextView instances inside a UICollectionView. Since UICollectionView recycles UITextView instances, the same UITextView might be reused in different rows, making the built-in UndoManager unreliable. The shouldChangeTextIn method in UITextViewDelegate is key to implementing undo/redo functionality properly. Here is an example of our implementation: extension ChecklistCell: UITextViewDelegate { func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { // Get the current text let s = textView.text ?? "" // Get the starting position of the change let start = range.location // Get the number of characters that will be replaced let count = range.length // Get the number of characters that will be added let after = text.count print(">>>> The current text = \"\(s)\"") print(">>>> The starting position of the change = \(start)") print(">>>> The number of characters that will be replaced = \(count)") print(">>>> The number of characters that will be added = \(after)") print(">>>>") if let delegate = delegate, let checklistId = checklistId, let index = delegate.checklistIdToIndex(checklistId) { delegate.attachTextAction(s: s, start: start, count: count, after: after, index: index) } return true } } Working scene behind the UITextViewDelegate However, this implementation does not work well with non-English input using an IME. When using an IME, there is an intermediate input before the final input is produced. For example, typing "wo" (intermediate input) produces "我" (final input). Currently, UITextViewDelegate captures both "wo" and "我". UITextViewDelegate captures both "wo" and "我" Is there a way to ignore the intermediate input from IME and only consider the final input? In Android, we use the beforeTextChanged method in TextWatcher to seamlessly ignore the intermediate input from IME and only consider the final input. You can see this in action in this Android captures only "我" Is there an equivalent way in iOS to ignore the intermediate input from IME and only take the final input into consideration?
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
0
Boosts
0
Views
580
Activity
Jun ’24
In-App Purchase Review Delaying App Release - Next Steps?
Our submitted binary app version has been approved by Apple reviewers. It's currently pending Developer Release. However, our in-app purchase, which was submitted at the same time, is still under review. Since the newly added in-app purchase hasn't been approved yet, we've decided to hold off on releasing the binary app version. Should we just wait for Apple to approve the in-app purchase, or are there any other recommended actions? Thank you.
Replies
2
Boosts
0
Views
813
Activity
Jun ’24
Not able login to simulator using the newly created sandbox account
I wish to login simulator using a sandbox account so that I can test on in-app purchase and subscription. I have created a new apple id account using a new email. I have confirmed the new apple id + password is correct, by login into https://appleid.apple.com/#!&page=signin I also added the new apple id account into App Store Connect as sandbox tester - However, I am still not able login to simulator. I am keep getting error - "Username or password is incorrect" You can see in the simulator background. I can login to https://appleid.apple.com/#!&page=signin using the new apple id + password via web browser. But, I am not sure why I am not able login into the simulator itself. Can anyone assist me on this? Thank you.
Replies
2
Boosts
4
Views
2.2k
Activity
Mar ’24
Can we perform filtering on PHPickerViewController, to exclude GIF?
For PHPickerViewController, we know we can perform simple filtering by var config = PHPickerConfiguration() config.filter = PHPickerFilter.images But, how about we only want to show images with format JPG & PNG, but excluding GIF? This is because our app doesn't support GIF. Is it possible to do so?
Replies
2
Boosts
0
Views
2.6k
Activity
Dec ’23
UICollectionView + UISearchController - How to achieve smooth scrolling animation when hiding search bar?
I notice that, normal implementation for UICollectionView + UISearchController, will not able to achieve smooth scrolling animation when hiding search bar. As you can see in the below video, when we scroll upward the page, it seems like "the page has slipped up suddenly". If we compare the animation with search bar in iOS Settings page, the problem is more obvious. iOS Settings page able to have a smooth scrolling experience. Implementation The following is our implementation. The complete workable project is found at : https://github.com/yccheok/demo-uicollectionview-uisearchcontroller class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate { @IBOutlet weak var collectionView: UICollectionView! let reuseIdentifier = "cell" // also enter this string as the cell identifier in the storyboard var items = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "bye"] private lazy var searchController: UISearchController = { let searchController = UISearchController(searchResultsController: UIViewController()) searchController.searchResultsUpdater = self searchController.obscuresBackgroundDuringPresentation = true searchController.searchBar.placeholder = "search_todos" searchController.searchBar.delegate = self return searchController }() override func viewDidLoad() { super.viewDidLoad() collectionView.dataSource = self collectionView.delegate = self navigationItem.searchController = searchController } // MARK: - UICollectionViewDataSource protocol // tell the collection view how many cells to make func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.items.count } // make a cell for each cell index path func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // get a reference to our storyboard cell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MyCollectionViewCell // Use the outlet in our custom class to get a reference to the UILabel in the cell cell.label.text = self.items[indexPath.item] cell.backgroundColor = .yellow return cell } // MARK: - UICollectionViewDelegate protocol func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { // handle tap events print("You selected cell #\(indexPath.item)!") } } class MyCollectionViewCell: UICollectionViewCell { @IBOutlet weak var label: UILabel! } extension ViewController: UISearchResultsUpdating { func updateSearchResults(for searchController: UISearchController) { } } extension ViewController: UISearchBarDelegate { } Do you have any idea, how we can achieve such a smooth scrolling animation? Thanks.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
0
Boosts
1
Views
896
Activity
Nov ’23
WidgetKit iOS 17 : Interactive widget no longer responsive after a long idle time
I have developed an interactive widget which looks as the following It is using CoreData. The view is implemented as the following struct widget_extensionEntryView : View { var body: some View { if let nsTodoList = nsTodoList { VStack(alignment: .leading, spacing: 1) { ... ForEach(0..<prefixGoodNSTodoArray.count, id: \.self) { index in ... let todoIntent = TodoIntent(objectIDAsString: objectIDAsString, parentOrder: parentOrder) Button(intent: todoIntent) { } } } } } } The AppIntent is implemented as the following import Foundation import AppIntents import WidgetKit import CoreData struct TodoIntent: AppIntent { static var title: LocalizedStringResource = "Complete Task" static var description: IntentDescription = IntentDescription("Complete selected task") @Parameter(title: "objectIDAsString") var objectIDAsString: String @Parameter(title: "parentOrder") var parentOrder: Int init() { } init(objectIDAsString: String, parentOrder: Int) { self.objectIDAsString = objectIDAsString self.parentOrder = parentOrder } func perform() async throws -> some IntentResult { guard let objectID = NSManagedObjectID.from(objectIDAsString) else { return .result() } guard let nsTodoList = NSTodoListRepository.INSTANCE.getNSTodoList(objectID) else { return .result() } nsTodoList.toggleChecked(context: CoreDataStack.INSTANCE.viewContext, parentOrder: Int64(parentOrder)) RepositoryUtils.saveContextIfPossible(CoreDataStack.INSTANCE.viewContext) TodoWidgetOptions.isWrittenByWidgetExtension = true // Refresh all home widgets. // TODO: https://developer.apple.com/forums/thread/721937 WidgetCenter.shared.reloadAllTimelines() return .result() } } The interactive widget works pretty well. However, tapping on the widget has no response in the following situations: After an overnight, we turn on the iPhone's screen and tap on the widget. After a few hours of idle time, we turn on the iPhone's screen and tap on the widget. One of the steps below will make the widget workable again: Launch and close the main app again. The main app will call WidgetCenter.shared.reloadAllTimelines() during sceneDidEnterBackground. Press and hold on the widget, choose 'Edit widget', and select the desired Todo list. One thing to take note of is that I am using IntentTimelineProvider instead of AppIntentTimelineProvider. The reason I am using 'older tech' is due to the limitation mentioned in https://developer.apple.com/forums/thread/741053 However, I am not sure whether this is the root cause of the problem. Does anyone have any idea why such a problem occurs? Thanks.
Replies
2
Boosts
1
Views
1.5k
Activity
Nov ’23
In iOS 17 AppIntent, how we can perform dynamic data selection for Widget?
Before iOS17, when we implement "Edit widget" feature with dynamic data, we are using the following ways. Using an intent definition file Using an intent extension Here's the outcome. A searchable view controller Multi sectioned data view controller Here is the implementation intent definition file + intent extension class IntentHandler: INExtension, ConfigurationIntentHandling { func provideStickyNoteWidgetItemOptionsCollection(for intent: ConfigurationIntent, with completion: @escaping (INObjectCollection<StickyNoteWidgetItem>?, Error?) -> Void) { var stickyNoteWidgetItems = [StickyNoteWidgetItem]() var archivedStickyNoteWidgetItems = [StickyNoteWidgetItem]() let allStickyNoteWidgetItems = NSPlainNoteRepository.getStickyNoteWidgetItemsWithoutTrash() for allStickyNoteWidgetItem in allStickyNoteWidgetItems { if allStickyNoteWidgetItem.archived == 1 { archivedStickyNoteWidgetItems.append(allStickyNoteWidgetItem) } else { stickyNoteWidgetItems.append(allStickyNoteWidgetItem) } } var sections = [INObjectSection<StickyNoteWidgetItem>]() if !stickyNoteWidgetItems.isEmpty { let section = INObjectSection(title: nil, items: stickyNoteWidgetItems) sections.append(section) } if !archivedStickyNoteWidgetItems.isEmpty { let archivedSection = INObjectSection(title: "archive".localized, items: archivedStickyNoteWidgetItems) sections.append(archivedSection) } let collection = INObjectCollection(sections: sections) completion(collection, nil) } override func handler(for intent: INIntent) -> Any { // This is the default implementation. If you want different objects to handle different intents, // you can override this and return the handler you want for that particular intent. return self } } However, if I were using AppIntent in iOS17, I can only achieve the following Not searchable. Not section-able. Using AppIntent import Foundation import AppIntents @available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) struct StickyNoteWidgetItemAppEntity: AppEntity { static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "StickyNoteWidgetItem") @Property(title: "archived") var archived: Bool? struct StickyNoteWidgetItemAppEntityQuery: EntityQuery { func entities(for identifiers: [StickyNoteWidgetItemAppEntity.ID]) async throws -> [StickyNoteWidgetItemAppEntity] { // TODO: return StickyNoteWidgetItemAppEntity entities with the specified identifiers here. return [] } func suggestedEntities() async throws -> [StickyNoteWidgetItemAppEntity] { // TODO: return likely StickyNoteWidgetItemAppEntity entities here. // This method is optional; the default implementation returns an empty array. return [ StickyNoteWidgetItemAppEntity(id: "id0", displayString: "note 0"), StickyNoteWidgetItemAppEntity(id: "id1", displayString: "note 1"), StickyNoteWidgetItemAppEntity(id: "id2", displayString: "note 2") ] } } static var defaultQuery = StickyNoteWidgetItemAppEntityQuery() var id: String // if your identifier is not a String, conform the entity to EntityIdentifierConvertible. var displayString: String var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(displayString)") } init(id: String, displayString: String) { self.id = id self.displayString = displayString } } By using AppIntent, I would like to achieve what is previously achievable using intent defination file + intent extension Is that ever possible? Thanks.
Replies
0
Boosts
2
Views
1.1k
Activity
Nov ’23
UISheetPresentationController: Remove Dim Background but Keep Touch-to-Dismiss?
I'm using the UISheetPresentationController to present a view, as shown in the following code: @IBAction func click(_ sender: Any) { let whiteViewController = WhiteViewController.instanceFromMainStoryBoard() if let sheet = whiteViewController.sheetPresentationController { sheet.detents = [.medium()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false sheet.prefersGrabberVisible = false // Remove dim effect. ////sheet.largestUndimmedDetentIdentifier = .medium } self.present(whiteViewController, animated: true) } The outcome is as follow This code results in a presentation with: A dimmed background. A 'Touch anywhere to dismiss' behavior. However, I want to remove the dim background while keeping the 'Touch anywhere to dismiss' functionality. When I add the line: sheet.largestUndimmedDetentIdentifier = .medium The dim background is indeed removed, but the touch-to-dismiss behavior is gone too, as illustrated: Is there a way to achieve both – removing the dim background and retaining the touch-to-dismiss functionality? Thank you.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
1
Boosts
0
Views
2.2k
Activity
Oct ’23
What kind of configuration is required, so that WKWebView able to render image files in AppGroup directory?
I have the following HTML string. We want to render image from our app AppGroup. <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <title>This is title</title> </head> <body> <p><h1>This is title</h1></p> <div style="font-size: 0"> <img src="file:///Users/xxx/Library/Developer/CoreSimulator/Devices/A7B89802-9C65-4512-85A7-51C4372172D0/data/Containers/Shared/AppGroup/14DA3695-BFAF-4096-9F54-2874FD8285C2/attachment/b16c714e-9bb5-4eaa-924e-e043a69088ea.jpeg" width="100%"> </div> This is body text </body> </html> However, if we execute the following code let html = ... // wkWebView is WKWebView wkWebView.loadHTMLString(html, baseURL: nil) Only text is rendered. The image is not rendered. May I know, what kind of configuration is required, so that WKWebView able to render image files in AppGroup directory? Thanks.
Topic: Safari & Web SubTopic: General Tags:
Replies
0
Boosts
0
Views
736
Activity
Apr ’23
Throw NSRangeException from NSFetchedResultsController.performFetch after introducing UICollectionViewDiffableDataSource
I was able to avoid NSInternalInconsistencyException by using NSDiffableDatasource with NSFetchedResultsController My data source definition is private typealias DataSource = UICollectionViewDiffableDataSource<Int, NSManagedObjectID> (Does anyone know why we should use Int as SectionIdentifierType? I find it works fine too, if I am using String as SectionIdentifierType) Even though I do not see NSInternalInconsistencyException anymore, I have the following crash log from Firebase Crashlytics. Do you know how to fix that? Fatal Exception: NSRangeException 0 CoreFoundation 0x99288 __exceptionPreprocess 1 libobjc.A.dylib 0x16744 objc_exception_throw 2 CoreFoundation 0x1a4318 -[__NSCFString characterAtIndex:].cold.1 3 CoreFoundation 0x928c8 -[NSArray subarrayWithRange:] 4 CoreData 0x702dc -[_PFArray subarrayWithRange:] 5 CoreData 0xc2d58 -[_NSDefaultSectionInfo objects] 6 CoreData 0x14eb98 -[NSFetchedResultsController _conditionallyDispatchSnapshotToDelegate:updatesInfo:] 7 CoreData 0xc3edc -[NSFetchedResultsController performFetch:] My NSFetchedResultsController look as following private lazy var fetchedResultsController: NSFetchedResultsController<NSPlainNote> = { let fetchRequest: NSFetchRequest<NSPlainNote> = NSPlainNote.fetchRequest( label: label, propertiesToFetch: ["pinned"] ) let controller = NSFetchedResultsController( fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.INSTANCE.viewContext, sectionNameKeyPath: "pinned", cacheName: nil ) controller.delegate = fetchedResultsControllerDelegate return controller }() My NSPlainNote.fetchResult looks as following static func fetchRequest(label: String?, propertiesToFetch: [Any]?) -> NSFetchRequest<NSPlainNote> { let fetchRequest = NSPlainNote.fetchRequest() fetchRequest.propertiesToFetch = propertiesToFetch fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "pinned", ascending: false), NSSortDescriptor(key: "order", ascending: true) ] if let label = label { let predicate = NSPredicate(format: "archived = false AND trashed = false AND label = %@", label) fetchRequest.predicate = predicate } else { let predicate = NSPredicate(format: "archived = false AND trashed = false") fetchRequest.predicate = predicate } return fetchRequest } I am not able to reproduce the problem. However, we can observe crash happens during NSFetchedResultsController.performFetch. For those who are experience in this, do you know what is the root cause, and how I can resolve such? Thanks.
Replies
0
Boosts
0
Views
921
Activity
Mar ’23
Does anyone know how I can reset/ cancel my in-app purchase status from TestFlight account?
I install and test my own app, in real device, from TestFlight. I want to able to keep testing on the in-app purchase/ subscription feature. I notice once I have purchase an item success (I was prompted to enter password during purchase), it will forever be marked as purchased. In simulator, if I uninstall then re-install again the app, the in-app purchase status will all be reset. However, for TestFlight in real device, I do not find a way to reset my purchase. (So that I can keep testing on the purchase process) Does anyone know, how I can reset/ cancel my in-app purchase status from TestFlight account?
Replies
1
Boosts
1
Views
996
Activity
Mar ’23
Providing in-note text search (UIFindInteraction) feature in iOS 15
I would like to implement in-note text search feature, as found in Apple's Notes, Apple's Safari app. It looks like the following I understand that such an API is called UIFindInteraction, and only available in iOS16. https://developer.apple.com/documentation/uikit/uifindinteraction WWDC 2022: Adopt desktop-class editing interactions However, my app is targeting iOS 15. I was wondering, is it possible for us to provide same feature, in iOS 15? Thank you.
Replies
0
Boosts
0
Views
974
Activity
Feb ’23