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

PullDown menu shows in reverse order when in landscape
I create a UIKit PullDown menu (in a positionMenu button), with the following code: func setupPullDownMenu() { let menu1 = UIAction(title: "Menu1") { [self] _ in // some code } let menu2 = UIAction(title: "Menu2") { [self] _ in // some code } let menu3 = UIAction(title: "Menu3") { [self] _ in // some code } let menu = UIMenu(title: "Positions", children: [menu1, menu2, menu3]) positionMenu.menu = menu positionMenu.showsMenuAsPrimaryAction = true }   In Portrait, options are listed as Menu1, Menu2, Menu3 But in Landscape it is the reverse: Menu3, Menu2, Menu1 I use Xcode 16.1ß and iOS 17.0 simulator Is it the expected behaviour ?
1
0
414
Aug ’24
Incorrect playback of video with Alpha in AVPlayerViewController
We appear to be experiencing a bug with the latest beta for visionOS, we are attempting to playback a video with a transparent background in the app. In the previous beta playback worked as expected and the transparent parts of the video were transparent. In the latest beta the background appears black. The view we are using in a SwiftUI wrapped version of AVPlayerViewController, we have narrowed the bug down to only occurring only when playback is being presented in the embedded experience mode, if playback is being done in the expanded experience then playback is as expected. This has only only been visible on an actual device, we have been unable to replicate the behaviour in the simulator using the latest Xcode 16.0 beta(beta 5 (16A5221g)) This is sample project that shows off the bug
1
0
422
Aug ’24
Show my app first in the list of apps in the UIActivityViewController share dialog for a custom file extension exported by my app
My app exports a custom file type identifier for a file that it exports to share between other users of the same app. However in the list of apps in UIActivityViewController view, my app is listed far off screen. How can I make my app which owns the file type appear first in the list, instead of seeing many irrelevant apps that can't actually open my file? Plist snippets: <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>My Custom App File</string> <key>LSHandlerRank</key> <string>Owner</string> <key>LSItemContentTypes</key> <array> <string>abc.myapp.myextension</string> </array> </dict> </array> <key>UTExportedTypeDeclarations</key> <array> <dict> <key>UTTypeConformsTo</key> <array> <string>public.content</string> <string>public.data</string> </array> <key>UTTypeDescription</key> <string>My Custom App File</string> <key>UTTypeIconFiles</key> <array/> <key>UTTypeIdentifier</key> <string>abc.myapp.myextension</string> <key>UTTypeTagSpecification</key> <dict> <key>public.filename-extension</key> <array> <string>myextension</string> </array> <key>public.mime-type</key> <array> <string>application/octet-stream</string> </array> </dict> </dict> </array>
0
0
285
Aug ’24
Tab bar for tvOS 18 is positioned too low (bug?)
I've noticed the tab bar in tvOS 18 (beta) is positioned lower on the TV screen than in previous versions. Bug? I see no documentation on this important UI change... If this is not a bug, is there any way to adjust the y coordinate of the tab bar location in tvOS 18? I would really like to restore this to the previous location for my app and avoid having to do OS-conditional constraints for all my views/pages.
2
0
392
Aug ’24
Critical Issue in iOS 18 Beta: UITabBarController Child View Controller Incorrectly Added as UITabBarItem, Leading to Application Crash
I am writing to report an issue I encountered with iOS 18 beta that affects my application, which has been available on the App Store for over two years and currently has over 60,000 active users. My application utilizes a UITabBarController to manage multiple tabs, where each tab hosts a UIViewController embedded within a UINavigationController. The application operates in two different states, where users may have either 5, 4, or 3 tabBarItems depending on their configuration. The issue arises when fewer than 5 tabs are present. In these cases, I add child view controllers to the UITabBarController to ensure they are displayed above the tab bar, rather than below it. The relevant code snippet is as follows: tabBarController.addChild(childController) tabBarController.view.addSubview(childController.view) Prior to iOS 18, this implementation functioned as expected. However, with the release of iOS 18, adding a child view controller to the UITabBarController results in the child being incorrectly added as a UITabBarItem. This misbehavior leads to an application crash when the unintended tab is selected. The crash trace is as follows: "Inconsistency in UITabBar items and view controllers detected. No view controller matches the UITabBarItem '<UITabBarItem: 0x142d9c480> selected'." I have attached screenshots from iOS 18 and previous versions to illustrate the issue, which compares the expected behavior in earlier iOS versions with the problematic behavior in iOS 18. I appreciate your attention to this matter and look forward to any guidance or resolution you can provide.
4
0
1.3k
Aug ’24
Xcode Error iMessage
Xcode Issue Hello all, I have encountered an error in the recent Xcode versions 15.4 and the version before that. When creating an iMessage App from the template menu and running the project out of the box. The simulator will fail to add the app and return and error: Failed to get pid info for pid 0: Operation not permitted This is something not local to me, don’t believe me try it out for yourself. The app will not work. I have looked endlessly online and have found nothing. If you see any posts on forums about this issue it is me. However I have noticed Apple’s IceCreamBuilder project works some how. https://developer.apple.com/documentation/messages/icecreambuilder-building-an-imessage-extension Any help or advice is appreciated thank you all.
0
0
254
Aug ’24
tvOS 18.0 Siri back button behavior bug
On testing my app with tvOS 18, I have noticed the Siri Remote back button no longer provides system-provided behavior when interacting with tab bar controller pages. Instead of moving focus back to the tab bar when pressed, the back button will close the app, as if the Home button was pressed. This occurs both on device and in the Simulator. Create tvOS project with a tab bar controller. Create pages/tabs which contain focusable items (ie. buttons) Scroll down to any focusable item (ie. a button or UICollectionView cell) Hit the Siri Remote back button. See expect behavior below: Expected behavior: System-provided behavior should move focus back to the tab bar at the top of the screen. Actual results: App is closed and user is taken back to the Home Screen. Has anyone else noticed this behavior?
4
1
626
Oct ’24
Deadlock in UIKit while injecting test bundle
Platform and Version iOS Development environment: Xcode 16.0 beta 5 (16A5221g), macOS 14.6.1 (23G93) Run-time configuration: iOS 18.0 beta 5 (22A5326g) Description of Problem Starting with iOS 18 SDK, test bundles containing a +load method that accesses UIScreen.mainScreen result in deadlock during test bundle injection. Also filed as FB14703057 I'm looking for clarity on whether this behavior is considered a bug in iOS or whether we will need to change the implementation of our app. Steps to Reproduce Create a new iOS app project using Objective-C and including unit tests Add the following code snippet to any .m file in the test target: @interface Foo: NSObject @end @implementation Foo + (void)load { UIScreen * const mainScreen = UIScreen.mainScreen; NSLog(@"%@", mainScreen); } @end Run the tests Expected Behavior As with iOS 17 & Xcode 15, the tests run to completion. Actual Behavior With iOS 18 & Xcode 16, deadlock during test bundle injection. stack_trace.txt
2
0
490
Sep ’24
Calling viewDidAppear intentionally
I am developing SDK and swizzling viewDidAppear. I have a customer who implements a custom TabBar Navigation where VC's are added to the hierarchy on the first load, and then, he changes the opacity to the currently displayed tab so the next time the user sees the tab - viewDidAppear isn't called, so my code isn't called. I'm attaching a sample project which reproduces that. Is there any way to trigger ViewDidAppear intentionally? If yes, what can be the side effect of doing that? Do I have any other alternative in this case? @main struct DemoCustomTabViewApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { TabBarRouterView() } } } import UIKit // MARK: - TabBarItem (unchanged) enum TabBarItem: Identifiable, CaseIterable { case home, search, profile var id: Self { self } var title: String { switch self { case .home: return "Home" case .search: return "Search" case .profile: return "Profile" } } var icon: String { switch self { case .home: return "house" case .search: return "magnifyingglass" case .profile: return "person" } } } // MARK: - NavigationControllerView struct NavigationControllerView: UIViewControllerRepresentable { var rootViewController: UIViewController func makeUIViewController(context: Context) -> UINavigationController { let navigationController = UINavigationController(rootViewController: rootViewController) return navigationController } func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {} } // MARK: - TabBarRouterViewModel class TabBarRouterViewModel: ObservableObject { @Published var currentTab: TabBarItem = .home @Published var cachedViews: [TabBarItem: AnyView] = [:] let tabs: [TabBarItem] = TabBarItem.allCases func switchTab(to tab: TabBarItem) { currentTab = tab } func createView(for tab: TabBarItem) -> AnyView { if let cachedView = cachedViews[tab] { return cachedView } let rootViewController: UIViewController switch tab { case .home: rootViewController = UIHostingController(rootView: Text("Home View")) case .search: rootViewController = UIHostingController(rootView: Text("Search View")) case .profile: rootViewController = UIHostingController(rootView: Text("Profile View")) } let navigationView = NavigationControllerView(rootViewController: rootViewController) let anyView = AnyView(navigationView) cachedViews[tab] = anyView return anyView } } // MARK: - CustomTabBarView (unchanged) struct CustomTabBarView: View { let tabs: [TabBarItem] @Binding var selectedTab: TabBarItem let onTap: (TabBarItem) -> Void var body: some View { HStack { ForEach(tabs) { tab in Spacer() VStack { Image(systemName: tab.icon) .font(.system(size: 24)) Text(tab.title) .font(.caption) } .foregroundColor(selectedTab == tab ? .blue : .gray) .onTapGesture { onTap(tab) } Spacer() } } .frame(height: 60) .background(Color.white) .shadow(radius: 2) } } // MARK: - TabBarRouterView struct TabBarRouterView: View { @StateObject private var viewModel = TabBarRouterViewModel() var body: some View { VStack(spacing: .zero) { contentView CustomTabBarView( tabs: viewModel.tabs, selectedTab: $viewModel.currentTab, onTap: viewModel.switchTab ) } .edgesIgnoringSafeArea(.bottom) } private var contentView: some View { ZStack { ForEach(viewModel.tabs) { tab in viewModel.createView(for: tab) .opacity(viewModel.currentTab == tab ? 1.0 : 0.0) } } } }
1
0
365
Aug ’24
Attempting to add scrubbing to UISlider
I made a custom slider by subclassing UISlider, and I'm trying to add scrubbing functionality to it, but for some reason the scrubbing is barely even noticeable at 0.1? In my code, I tried multiplying change in x distance by the scrubbing value, but it doesn't seem to work. Also, when I manually set the scrubbing speed to a lower value such as 0.01, it does go slower but it looks really laggy and weird?? What am I doing wrong? Any help or advice would be greatly appreciated! Subclass of UISlider: class SizeSliderView: UISlider { private var previousLocation: CGPoint? private var currentLocation: CGPoint? private var translation: CGFloat = 0 private var scrubbingSpeed: CGFloat = 1 private var defaultDiameter: Float init(startValue: Float = 0, defaultDiameter: Float = 500) { self.defaultDiameter = defaultDiameter super.init(frame: .zero) value = clamp(value: startValue, min: minimumValue, max: maximumValue) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func draw(_ rect: CGRect) { super.draw(rect) clear() createThumbImageView() addTarget(self, action: #selector(valueChanged(_:)), for: .valueChanged) } // Clear elements private func clear() { tintColor = .clear maximumTrackTintColor = .clear backgroundColor = .clear thumbTintColor = .clear } // Call when value is changed @objc private func valueChanged(_ sender: SizeSliderView) { CATransaction.begin() CATransaction.setDisableActions(true) CATransaction.commit() createThumbImageView() } // Create thumb image with thumb diameter dependent on thumb value private func createThumbImageView() { let thumbDiameter = CGFloat(defaultDiameter * value) let thumbImage = UIColor.red.circle(CGSize(width: thumbDiameter, height: thumbDiameter)) setThumbImage(thumbImage, for: .normal) setThumbImage(thumbImage, for: .highlighted) setThumbImage(thumbImage, for: .application) setThumbImage(thumbImage, for: .disabled) setThumbImage(thumbImage, for: .focused) setThumbImage(thumbImage, for: .reserved) setThumbImage(thumbImage, for: .selected) } // Return true so touches are tracked override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { let location = touch.location(in: self) // Ensure that start location is on thumb let thumbDiameter = CGFloat(defaultDiameter * value) if location.x < bounds.width / 2 - thumbDiameter / 2 || location.x > bounds.width / 2 + thumbDiameter / 2 || location.y < 0 || location.y > thumbDiameter { return false } previousLocation = location super.beginTracking(touch, with: event) return true } // Track based on moving slider override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { guard isTracking else { return false } guard let previousLocation = previousLocation else { return false } // Reference // location: location of touch relative to device // delta location: change in touch location WITH scrubbing // adjusted location: location of touch to slider bounds (WITH scrubbing) // translation: location of slider relative to device let location = touch.location(in: self) currentLocation = location scrubbingSpeed = getScrubbingSpeed(for: location.y - 50) let deltaLocation = (location.x - previousLocation.x) * scrubbingSpeed var adjustedLocation = deltaLocation + previousLocation.x - translation if adjustedLocation < 0 { translation += adjustedLocation adjustedLocation = deltaLocation + previousLocation.x - translation } else if adjustedLocation > bounds.width { translation += adjustedLocation - bounds.width adjustedLocation = deltaLocation + previousLocation.x - translation } self.previousLocation = CGPoint(x: deltaLocation + previousLocation.x, y: location.y) let newValue = Float(adjustedLocation / bounds.width) * (maximumValue - minimumValue) + minimumValue setValue(newValue, animated: false) sendActions(for: .valueChanged) return true } // Reset start and current location override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { self.currentLocation = nil self.translation = 0 super.touchesEnded(touches, with: event) } // Thumb location follows current location and resets in middle override func thumbRect(forBounds bounds: CGRect, trackRect rect: CGRect, value: Float) -> CGRect { let thumbDiameter = CGFloat(defaultDiameter * value) let origin = CGPoint(x: (currentLocation?.x ?? bounds.width / 2) - thumbDiameter / 2, y: (currentLocation?.y ?? thumbDiameter / 2) - thumbDiameter / 2) return CGRect(origin: origin, size: CGSize(width: thumbDiameter, height: thumbDiameter)) } private func getScrubbingSpeed(for value: CGFloat) -> CGFloat { switch value { case 0: return 1 case 0...50: return 0.5 case 50...100: return 0.25 case 100...: return 0.1 default: return 1 } } private func clamp(value: Float, min: Float, max: Float) -> Float { if value < min { return min } else if value > max { return max } else { return value } } } UIView representative: struct SizeSlider: UIViewRepresentable { private var startValue: Float private var defaultDiameter: Float init(startValue: Float, defaultDiameter: Float) { self.startValue = startValue self.defaultDiameter = defaultDiameter } func makeUIView(context: Context) -> SizeSliderView { let view = SizeSliderView(startValue: startValue, defaultDiameter: defaultDiameter) view.minimumValue = 0.1 view.maximumValue = 1 return view } func updateUIView(_ uiView: SizeSliderView, context: Context) {} } Content view: struct ContentView: View { var body: some View { SizeSlider(startValue: 0.20, defaultDiameter: 100) .frame(width: 400) } }
7
0
447
Aug ’24
iPadOS 18 Tab Bar Transitions
Tab bars on iPadOS 18 have moved to the top of the screen. They now share space with navigation bars. We have added calls to setTabBarHidden(_:animated:) alongside existing calls to setNavigationBarHidden(_:animated:) in pushed view controller's viewWillAppear(_:) methods to manage the appearance of the tab bar and navigation bar within navigation controllers. This results in layout issues with the safe area and navigation bar. I've attached screenshots from an example app demonstrating the issue. How can we manage the appearance of both the navigation bar and tab bar so that they share the same space when visible, but are properly hidden and excluded from the safe area when not? /// The root view controller shows both the navigation bar and tab bar class ViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(false, animated: animated) tabBarController?.setTabBarHidden(false, animated: animated) } } /// The second view controller hides both the navigation bar and tab bar class ViewController2: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(true, animated: animated) tabBarController?.setTabBarHidden(true, animated: animated) } @IBAction func customBackButtonTapped(_ sender: Any) { navigationController?.popViewController(animated: true) } } /// The third view controller shows the navigation bar but hides the tab bar class ViewController3: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(false, animated: animated) tabBarController?.setTabBarHidden(true, animated: animated) } }
1
2
992
Aug ’24
Receiving NSMallocException on Real devices while using DrawPDFPage
Hi We're using CGPDFDocument to retrieve the PDF document and draw it in the UIView using CGContext.DrawPDFPage. While doing this operation, I was able to get an NSMallocException with a specific document. I was able to get this exception only on real devices; emulators are working fine. The device used is the iPad (5th generation), which has iOS 16.7.8. Steps to replicate: Open the application at the link (we have attached the sample with PDF file with the issue in the link) https://www.dropbox.com/scl/fi/mosvcnqc0nvxwdjf8k0m5/SimplePDFVieweriOS.zip?rlkey=0ntj75yjg71kjwtwwe06uax5v&st=mmv08e2f&dl=0 Navigate through the pages using the next button. After the page navigation to page 7, and the application will throw an NSMallocException with the reason: Failed to grow buffer Platform: Xamarin.iOS Development tool: Visual Studio 2022 Stack trace for the issue: Native stack trace: 0 CoreFoundation 0x00000001b32e9418 55B9BA28-4C5C-3FE7-9C47-4983337D6E83 + 37912 1 libobjc.A.dylib 0x00000001ac63dc28 objc_exception_throw + 56 2 CoreFoundation 0x00000001b347c5c8 55B9BA28-4C5C-3FE7-9C47-4983337D6E83 + 1689032 3 CoreFoundation 0x00000001b3474504 55B9BA28-4C5C-3FE7-9C47-4983337D6E83 + 1656068 4 CoreFoundation 0x00000001b3363160 __CFSafelyReallocate + 68 5 CoreFoundation 0x00000001b33aae9c 55B9BA28-4C5C-3FE7-9C47-4983337D6E83 + 831132 6 CoreFoundation 0x00000001b335af2c CFDataSetLength + 152 7 CoreGraphics 0x00000001b4ca71a4 CGDataProviderCopyData + 240 8 ImageIO 0x00000001b7fafd38 7E9A543E-EE3C-34B7-9EFE-D5F6357F2FA3 + 32056 9 ImageIO 0x00000001b7fb1124 7E9A543E-EE3C-34B7-9EFE-D5F6357F2FA3 + 37156 10 ImageIO 0x00000001b7ff7124 7E9A543E-EE3C-34B7-9EFE-D5F6357F2FA3 + 323876 11 ImageIO 0x00000001b7fb0fe0 CGImageSourceCreateWithDataProvider + 176 12 CoreGraphics 0x00000001b4d7ac0c EDFE1CE3-C3C4-30A2-AC92-099D20347781 + 1018892 13 CoreGraphics 0x00000001b4d7a9a0 CGPDFImageCreateImage + 208 14 CoreGraphics 0x00000001b4decbc8 EDFE1CE3-C3C4-30A2-AC92-099D20347781 + 1485768 15 CoreGraphics 0x00000001b4d26e34 EDFE1CE3-C3C4-30A2-AC92-099D20347781 + 675380 16 CoreGraphics 0x00000001b4d2f7c4 EDFE1CE3-C3C4-30A2-AC92-099D20347781 + 710596 17 CoreGraphics 0x00000001b4ce16dc CGPDFScannerScan + 436 18 CoreGraphics 0x00000001b5073198 CGPDFDrawingContextDraw + 112 19 CoreGraphics 0x00000001b4d271b0 EDFE1CE3-C3C4-30A2-AC92-099D20347781 + 676272 20 CoreGraphics 0x00000001b4d2f7c4 EDFE1CE3-C3C4-30A2-AC92-099D20347781 + 710596 21 CoreGraphics 0x00000001b4ce16dc CGPDFScannerScan + 436 22 CoreGraphics 0x00000001b4d3f68c CGContextDrawPDFPageWithDrawingCallbacks + 2680 23 CoreGraphics 0x00000001b4cdb04c CGContextDrawPDFPage + 32 24 SimplePDFViewer 0x0000000100f0cb90 SimplePDFViewer + 1969040 25 SimplePDFViewer 0x0000000100eeb3c0 SimplePDFViewer + 1831872 26 SimplePDFViewer 0x0000000100d35b14 SimplePDFViewer + 39700 27 SimplePDFViewer 0x0000000100e8eb80 SimplePDFViewer + 1452928 28 SimplePDFViewer 0x0000000100f483f4 mono_pmip + 25092 29 SimplePDFViewer 0x0000000100fd8b90 mono_pmip + 616864 30 SimplePDFViewer 0x0000000100fdc014 mono_pmip + 630308 31 SimplePDFViewer 0x0000000100d32450 SimplePDFViewer + 25680 32 SimplePDFViewer 0x0000000100d32328 SimplePDFViewer + 25384 33 UIKitCore 0x00000001b5412150 27A9C298-B702-3C39-8C06-07196E4CD16B + 1667408 34 QuartzCore 0x00000001b47990e8 9E40B2EB-260C-3AF1-AE45-A1A551A8C1B9 + 119016 35 QuartzCore 0x00000001b480adb8 9E40B2EB-260C-3AF1-AE45-A1A551A8C1B9 + 585144 36 QuartzCore 0x00000001b47987a8 9E40B2EB-260C-3AF1-AE45-A1A551A8C1B9 + 116648 37 QuartzCore 0x00000001b4798034 9E40B2EB-260C-3AF1-AE45-A1A551A8C1B9 + 114740 38 QuartzCore 0x00000001b47a89f0 9E40B2EB-260C-3AF1-AE45-A1A551A8C1B9 + 182768 39 QuartzCore 0x00000001b47d5dc0 9E40B2EB-260C-3AF1-AE45-A1A551A8C1B9 + 368064 40 QuartzCore 0x00000001b47c0dc8 9E40B2EB-260C-3AF1-AE45-A1A551A8C1B9 + 282056 41 CoreFoundation 0x00000001b33698e8 55B9BA28-4C5C-3FE7-9C47-4983337D6E83 + 563432 42 CoreFoundation 0x00000001b32f951c 55B9BA28-4C5C-3FE7-9C47-4983337D6E83 + 103708 43 CoreFoundation 0x00000001b3355214 55B9BA28-4C5C-3FE7-9C47-4983337D6E83 + 479764 44 CoreFoundation 0x00000001b3359d20 CFRunLoopRunSpecific + 584 45 GraphicsServices 0x00000001eab69998 GSEventRunModal + 160 46 UIKitCore 0x00000001b55ec448 27A9C298-B702-3C39-8C06-07196E4CD16B + 3609672 47 UIKitCore 0x00000001b55ec0c0 UIApplicationMain + 312 48 SimplePDFViewer 0x000000010106b350 xamarin_UIApplicationMain + 60 49 SimplePDFViewer 0x0000000100f0de18 SimplePDFViewer + 1973784 50 SimplePDFViewer 0x0000000100eecd4c SimplePDFViewer + 1838412 51 SimplePDFViewer 0x0000000100eece70 SimplePDFViewer + 1838704 52 SimplePDFViewer 0x0000000100d33ad0 SimplePDFViewer + 31440 53 SimplePDFViewer 0x0000000100e8eb80 SimplePDFViewer + 1452928 54 SimplePDFViewer 0x0000000100f483f4 mono_pmip + 25092 55 SimplePDFViewer 0x0000000100fd8b90 mono_pmip + 616864 56 SimplePDFViewer 0x0000000100fddbdc mono_pmip + 637420 57 SimplePDFViewer 0x0000000100f2d5ec SimplePDFViewer + 2102764 58 SimplePDFViewer 0x00000001010802dc xamarin_log + 22464 59 SimplePDFViewer 0x0000000100d334bc SimplePDFViewer + 29884 60 dyld 0x00000001d0b04344 8A4B89B7-D348-375B-97B1-FC8A84E3E5CE + 82756 Kindly let us know if you need any further details regarding this issue.
0
0
268
Aug ’24
Convert view to PDF
I'm trying to convert a view (ScrollView) to PDF, on my physical device iPhone 7 with iOS 15.8 it works perfectly, but on my friends' iPhone 11 and iPhone 13 both with iOS 17+ the application crashes at the time of conversion, it also crashes in the xCode preview and in the Simulators without showing errors. I call the function like this: Button(action: { if included && !saving { if signatureImage != nil { exportPDF(name: "ToR-(patient.name)-(patient.id)") { self } completion: { status, url in if let url = url, status { self.PDFUrl = url self.showActionSheet.toggle() print("URL => (url) - Status (status)") } else { print("Failed") } } } else { self.scrollToBottom = true } } }) { if saving { ProgressView() .tint(Color("MainColor")) .scaleEffect(1.5) } else { Text(NSLocalizedString("Save", comment: "")) } } And the function is this: import SwiftUI import UIKit extension View { func convertToScrollView<Content: View>(@ViewBuilder content: @escaping () -> Content) -> UIScrollView { let scrollView = UIScrollView() let hostingController = UIHostingController(rootView: content()).view! hostingController.translatesAutoresizingMaskIntoConstraints = false let constraints = [ hostingController.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), hostingController.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), hostingController.topAnchor.constraint(equalTo: scrollView.topAnchor), hostingController.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), hostingController.widthAnchor.constraint(equalToConstant: screenBounds().width) ] scrollView.addSubview(hostingController) scrollView.addConstraints(constraints) scrollView.layoutIfNeeded() return scrollView } func exportPDF<Content: View>(name: String, @ViewBuilder content: @escaping () -> Content, completion: @escaping (Bool, URL?) -> ()) { let documentDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! let outputFileURL = documentDirectory.appendingPathComponent("\(name).pdf") let pdfView = convertToScrollView { content() } pdfView.tag = 1009 let size = pdfView.contentSize pdfView.frame = CGRect(x: 0, y: getSafeArea().top, width: size.width, height: size.height) getRootController().view.insertSubview(pdfView, at: 0) let renderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: size.width, height: size.height)) do { try renderer.writePDF(to: outputFileURL, withActions: { context in context.beginPage() pdfView.layer.render(in: context.cgContext) }) completion(true, outputFileURL) } catch { completion(false, nil) print(error.localizedDescription) } getRootController().view.subviews.forEach { view in if view.tag == 1009 { view.removeFromSuperview() } } //pdfView.removeFromSuperview() } func screenBounds() -> CGRect { return UIScreen.main.bounds } func getRootController() -> UIViewController { guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return .init() } guard let root = screen.windows.first?.rootViewController else { return .init() } return root } func getSafeArea() -> UIEdgeInsets { guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return .zero } guard let safeArea = screen.windows.first?.safeAreaInsets else { return .zero } return safeArea } }
4
0
453
Aug ’24
Present Modal View Error when iPhone enable screen mirroring
In one of our SwiftUI projects, we intensively use UIViewController to present a SwiftUI View modally, and it works perfectly under normal circumstances. However, we have observed that when screen mirroring is enabled on the iPhone, the @Environment viewControllerHolder becomes nil, preventing the proper presentation of another view. Xcode (14.5) does not flag any issues with the code, and our project is set to build for iOS 17.5. Without changing too many codebase, is there a way to fix this unexpected issue? import SwiftUI import UIKit struct ContentView: View { @Environment(\.viewController) private var viewControllerHolder: UIViewController? @State var presentSecondPage = false var body: some View { VStack(spacing: 40) { Text("This is First Page") Button("Present Second Page") { presentSecondPage = true } } .onChange(of: presentSecondPage) { if presentSecondPage { viewControllerHolder?.present(style: .fullScreen) { SecondPage(presentSecondPage: $presentSecondPage) } } } } } struct SecondPage: View { @Environment(\.viewController) private var viewControllerHolder: UIViewController? @Binding var presentSecondPage: Bool var body: some View { VStack(spacing: 40) { Text("This is Second Page") Button("Back to First Page") { presentSecondPage = false viewControllerHolder?.dismiss(animated: true) } } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.gray) } } struct ViewControllerHolder { weak var value: UIViewController? } struct ViewControllerKey: EnvironmentKey { static var defaultValue: ViewControllerHolder { let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene let rootVC = windowScene?.windows.first(where: { $0.isKeyWindow })?.rootViewController return ViewControllerHolder(value: rootVC) } } extension EnvironmentValues { var viewController: UIViewController? { get { return self[ViewControllerKey.self].value } set { self[ViewControllerKey.self].value = newValue } } } extension UIViewController { func present<Content: View>(style: UIModalPresentationStyle = .automatic, @ViewBuilder builder: () -> Content) { let toPresent = UIHostingController(rootView: AnyView(EmptyView())) toPresent.modalPresentationStyle = style toPresent.rootView = AnyView( builder() .environment(\.viewController, toPresent) ) NotificationCenter.default.addObserver(forName: Notification.Name(rawValue: "dismissModal"), object: nil, queue: nil) { [weak toPresent] _ in toPresent?.dismiss(animated: true, completion: nil) } self.present(toPresent, animated: true, completion: nil) } } Normal Circumstances Screen Mirroring
1
0
326
Aug ’24
UICollectionViewLayout unexpected animations when cells contain AutoLayout views with custom height
The code for the issue is attached below. Hello, I am trying to implement a custom UICollectionViewLayout that does the following: Everything works great for the most part, however I have encountered some unexpected animations when applying a new snapshot: As you can see, any cell that contains a custom view with a height set with AutoLayout is scaled vertically before animating to it's intended height. Here is a simple Xcode project that demonstrates the issue. Tap on the plus sign in the top right corner and watch the cells. Example project: https://we.tl/t-9Y25NHzxiI Custom UICollectionViewLayout code: final class CustomLayout: UICollectionViewLayout { struct PMCardContainerLayoutCell: Equatable { var column: Int var row: Int } // Configurable properties public var numberOfColumns: Int = 6 public var cellHeight: Double = 100 public var cellSpacing: Double = 20 public var rowSpacing: Double = 20 public var sectionInsets: NSDirectionalEdgeInsets = .zero public var layoutAttributes: [IndexPath: UICollectionViewLayoutAttributes] = [:] override func prepare() { super.prepare() guard let collectionView else { return } var updatedLayoutAttributes: [IndexPath: UICollectionViewLayoutAttributes] = [:] let columnWidth: Double = (collectionView.bounds.width - cellSpacing * Double(numberOfColumns - 1) - sectionInsets.leading - sectionInsets.trailing) / Double(numberOfColumns) let numberOfSections: Int = collectionView.numberOfSections for section in 0..<numberOfSections { var occupiedCells: [PMCardContainerLayoutCell] = [] var currentColumn: Int = 0 var currentRow: Int = 0 let numberOfItems: Int = collectionView.numberOfItems(inSection: section) for item in 0..<numberOfItems { let itemIndexPath = IndexPath(item: item, section: section) let itemAttributes = UICollectionViewLayoutAttributes(forCellWith: itemIndexPath) let itemSpanColumn = 1 let itemHeight = layoutAttributes[itemIndexPath]?.bounds.height ?? 140 let itemSpanRow = Int(ceil(itemHeight / (cellHeight + rowSpacing))) let itemWidth = columnWidth * Double(itemSpanColumn) + cellSpacing * (Double(itemSpanColumn) - 1) while true { var itemDoesFit: Bool = true if currentColumn + itemSpanColumn > numberOfColumns { currentColumn = 0 currentRow += 1 } for cell in 0..<itemSpanColumn { if occupiedCells.contains(.init(column: currentColumn + cell, row: currentRow)) { itemDoesFit = false } } if itemDoesFit { break } currentColumn += itemSpanColumn } if itemSpanRow > 1 { for row in 1..<itemSpanRow { for column in 0..<itemSpanColumn { occupiedCells.append(.init(column: currentColumn + column, row: currentRow + row)) } } } let originX = sectionInsets.leading + columnWidth * Double(currentColumn) + cellSpacing * Double(currentColumn) let originY = + cellHeight * Double(currentRow) + rowSpacing * Double(currentRow) itemAttributes.frame = CGRect( x: originX, y: originY, width: itemWidth, height: itemHeight ) itemAttributes.zIndex = itemIndexPath.section * 10 + itemIndexPath.item updatedLayoutAttributes[itemIndexPath] = itemAttributes currentColumn += itemSpanColumn } } layoutAttributes = updatedLayoutAttributes } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var allAttributes: [UICollectionViewLayoutAttributes] = [] for (_, attributes) in layoutAttributes { if (rect.intersects(attributes.frame)) { allAttributes.append(attributes) } } return allAttributes } override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { return layoutAttributes[indexPath] } override var collectionViewContentSize: CGSize { guard let collectionView else { return .zero } let contentHeight: CGFloat = layoutAttributes.map({ $0.value.frame.maxY }).max() ?? 0 return CGSize(width: collectionView.bounds.width, height: contentHeight) } override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true } override func shouldInvalidateLayout(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> Bool { return originalAttributes.frame.height != preferredAttributes.frame.height } override func invalidationContext(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutInvalidationContext { layoutAttributes[preferredAttributes.indexPath]?.frame.size = preferredAttributes.frame.size let context = super.invalidationContext(forPreferredLayoutAttributes: preferredAttributes, withOriginalAttributes: originalAttributes) return context } public override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) { super.invalidateLayout(with: context) if context.invalidateEverything || context.invalidateDataSourceCounts { layoutAttributes.removeAll() } } } Anyone have any idea what I am doing wrong? Thank you!
1
0
282
Aug ’24
Unexpected animation from UICalendarView embedded in UIStackView
I have a UICalendarView that is embedded in a UIStackView. When I hide/show items in the stack and cause the height of the stack to change, the UICalendarView animates unexpectedly: Note that the height and width of the UICalendarView remain unchanged and the animation appears to leave the actual content unchanged. Interestingly, if I select a month with 6 weeks it does not do this animation: Nothing I have tried has allowed me to avoid this distracting and unnecessary animation. Any thoughts as to why this is happening or, even better, is there anything I can do to avoid it? Appreciate the help!
1
0
282
Aug ’24
UICollectionView internal inconsistency: missing final attributes for cell
Hi, I'm running into a crash I can't wrap my head around. I'm using a collectionView with a compositional layout. Upon reloading the collection view via reloadData or reloading a particular section via reloadSection, customers are running into a crash I'm unable to reproduce. The only information is this: Fatal Exception: NSInternalInconsistencyException UICollectionView internal inconsistency: missing final attributes for cell <UICollectionViewListCell: 0x13ad42a40; frame = (0 791; 768 40); layer = <CALayer: 0x28268e0e0>>; initial attributes: <UICollectionViewLayoutAttributes: 0x13ac5aa90> index path: (<NSIndexPath: 0xb33e6ceee91dbb86> {length = 2, path = 4 - 0}); frame = (0 791; 768 40); ; layout query: <UICollectionViewLayoutAttributes: 0x137db5190> index path: (<NSIndexPath: 0xb33e6ceee91dbb86> {length = 2, path = 4 - 0}); frame = (0 800; 768 44); ; collection view: <UICollectionView: 0x13d120a00; frame = (0 0; 768 904); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x2829ad7d0>; layer = <CALayer: 0x2827f1cc0>; contentOffset: {0, 0}; contentSize: {768, 1715}; adjustedContentInset: {0, 0, 0, 0}; layout: <UICollectionViewCompositionalLayout: 0x13ad2bd50>; dataSource: <drchrono_EHR.AppointmentDetailViewController: 0x139fff600>> Any help would be greatly appreciated. Thank you.
0
0
303
Aug ’24
UISearchBar placeHolder and leftView is dissappearing.
I have UISearchBar, when I run from xCode placeholder and icon is working well but when i run from simulator or testflight placeholder and icon is dissappear. How can I solve it? Here my code: view: private lazy var searchBar: UISearchBar = { let view = UISearchBar() view.delegate = self view.barTintColor = UIColor.clear view.backgroundColor = UIColor.clear view.isTranslucent = true view.setBackgroundImage(UIImage(), for: .any, barMetrics: .default) view.placeholder = "Search" return view }() setup method: view.addSubview(searchBar) view.addSubview(collectionView) searchBar.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide).inset(17) make.leading.trailing.equalToSuperview().inset(6) make.height.equalTo(40) }
1
0
247
Aug ’24
UIImagePickerController inside of a SwiftUI View dismisses the view, rather than firing the callback.
Hello, When using a UIImagePickerController with the .camera configuration I'm currently facing an issue where the delegate function imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) is not firing. But rather UIKit internals dismisses the parent view to the UIImagePickerController. I'm showing the picker controller through a UIViewControllerRepresentable. It does not always occur however, and the behavior is very flakey, sometimes it fires when pressing the b, sometimes it does not. When setting a breakpoint at the dismiss function when pressing the "Use Photo" button, UIKit internals dismisses the view, not my own code.
1
0
405
Aug ’24