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

Full width UICollectionViewCells overlap during interface orientation rotations causing ugly animation
Preface Upon rotating the interface, the UICollectionViewCells overlap, generating an unpleasant animation that for sure can't be used in production. The code The code was executed on iPhone 6S (NN0W2TU/A A1688) with iOS 15.8.2. I could reproduce the issue on iPhone 15 Pro with iOS 17 on simulator as well. SelfConfiguringCell.swift: import UIKit protocol SelfConfiguringCell: UICollectionViewCell { static var reuseIdentifier: String { get } func configure(with image: String) } ISVImageScrollView.swift: Code here CarouselCell.swift: import UIKit import SnapKit class CarouselCell: UICollectionViewCell, SelfConfiguringCell, UIScrollViewDelegate { static var reuseIdentifier: String = "carousel.cell" internal var image: String = "placeholder" { didSet { self.imageView = UIImageView(image: UIImage(named: image)) self.scrollView.imageView = self.imageView } } let scrollView: ISVImageScrollView = { let scrollView = ISVImageScrollView() scrollView.minimumZoomScale = 1.0 scrollView.maximumZoomScale = 30.0 scrollView.zoomScale = 1.0 scrollView.contentOffset = .zero scrollView.bouncesZoom = true return scrollView }() var imageView: UIImageView = { let image = UIImage(named: "placeholder")! let imageView = UIImageView(image: image) return imageView }() func setImage(_ image: String) { self.image = image } func configure(with image: String) { self.setImage(image) self.scrollView.snp.makeConstraints { make in make.left.top.right.bottom.equalTo(contentView) } } override init(frame: CGRect) { super.init(frame: frame) contentView.backgroundColor = UIColor.black scrollView.delegate = self scrollView.imageView = self.imageView contentView.addSubview(scrollView) } required init?(coder: NSCoder) { fatalError("Cannot init from storyboard") } func viewForZooming(in scrollView: UIScrollView) -> UIView? { return self.imageView } } ViewController: import UIKit class ViewController: UICollectionViewController { var currentPage: IndexPath? = nil let images = ["police", "shutters", "depot", "cakes", "sign"] init() { let compositionalLayout = UICollectionViewCompositionalLayout { sectionIndex, environment in let absoluteW = environment.container.effectiveContentSize.width let absoluteH = environment.container.effectiveContentSize.height // Handle landscape if absoluteW > absoluteH { print("landscape") let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1) ) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group) return section } else { // Handle portrait print("portrait") let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(absoluteW * 9.0/16.0) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(absoluteW * 9.0/16.0) ) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group) return section } } let config = UICollectionViewCompositionalLayoutConfiguration() config.interSectionSpacing = 0 config.scrollDirection = .horizontal compositionalLayout.configuration = config super.init(collectionViewLayout: compositionalLayout) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() collectionView.delegate = self collectionView.dataSource = self collectionView.isPagingEnabled = true // Register cell for reuse collectionView.register(CarouselCell.self, forCellWithReuseIdentifier: CarouselCell.reuseIdentifier) } override func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.images.count } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let reusableCell = collectionView.dequeueReusableCell(withReuseIdentifier: CarouselCell.reuseIdentifier, for: indexPath) as? CarouselCell else { fatalError() } let index : Int = (indexPath.section * self.images.count) + indexPath.row reusableCell.configure(with: self.images[index]) return reusableCell } } Notes I found a similar unanswered question here. I'm sure something can be done about it because if I switch to SwiftUI with a TabView, that according to SwiftUI Introspect documentation for TabViewWithPageStyleType, is using UICollectionView under the hood, I'm not getting that ugly animation anymore. Though I can't switch to SwiftUI to use TabView because on interface rotation it loses the page index (well known bug, see here), which probably is even trickier to workaround.
0
0
194
2w
UITableView indexPathsForVisibleRows get incorrect
I have a UITableView with 1 section and 3 rows, and each row is 1000 height, the UITableView height is 832, such as below: but when I click the right button, the visibleCells has 2 items(row-0 and row-1), but the indexPathsForVisibleRows only has 1 item(row-0). In my expectation, I set UITableView contentOffset with 168.8, the row-1 cell is visible. but indexPathsForVisibleRows does not correct. Well then, I try to read the assembly of indexPathsForVisibleRows, I found that the UITableView use _visibleBounds to compute indexPathsForVisibleRows, but the _visibleBounds.height is reduced by one when compute, why? the assembly code looks like below:
0
0
163
2w
Attach virtual keyboard to project in Adobe XD
I'm creating a slate pad for Ipad with several input fields to fill using Adobe XD. How can I obtain and attach a virtual keyboard to fill these input fields? I found a video with a similar concept but on Iphone: https://www.youtube.com/watch?v=O7tPI0VuKd4&list=PLWeIX-zcNnqII-nzviv_yxT2WNQRRYbiG&index=3 In this other link, the video mentions that the virtual keyboard was obtained from a UI kit that Apple provides. But I've looked and looked and I can't find a page to download this virtual keyboard. Any ideas? https://www.youtube.com/watch?v=rosf7-zVYjY&t=122s
0
0
239
3w
UIBarButtonItem: show 'menu' programmatically
Hi, I have a UIBarButtonItem that I create with a UIMenu, and it's added to the navigation bar: self.rightButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd menu: [self createMenuForAddItem]]; self.navigationItem.rightBarButtonItems = @[self.rightButton]; The menu shows fine when a user clicks on the bar button. Now I want to also show this menu programmatically, for e.g if the user opens the app for the 5th time, the menu from the bar button item shows automatically, without the user having to explicitly press the button. How do I achieve this?
1
0
164
3w
SwiftUI Image(uiImage:) converts colored image to black and white
I have a data object that dynamically changes the UIImage assigned to one of its instance variables, but when showing this image in SwiftUI, it's always black and white. The following sample code shows the difference between the same image, but using first the native constructor Image(systemName:) and then Image(uiImage:). When using AppKit and Image(nsImage:) this issue doesn't happen. import SwiftUI import UIKit struct ContentView: View { @State var object = MyObject() var body: some View { Image(systemName: "exclamationmark.triangle.fill") .symbolRenderingMode(.palette) .foregroundStyle(.white, .yellow) Image(uiImage: object.image) } } class MyObject { var image = UIImage(systemName: "exclamationmark.triangle.fill")! .applyingSymbolConfiguration(.init(paletteColors: [.white, .systemYellow]))! } #Preview { ContentView() }
0
0
158
3w
WKWebView failing in 17.5 beta
hi I have been using WKWebView embedded in a UIViewRepresentable for displaying inside a SwiftUI View hierarchy, but when I try the same code on 17,5 beta (simulator) the code fails. In fact, the code runs (no exceptions raised or anything) but the web view does not render. In the console logs I see: Warning: -[BETextInput attributedMarkedText] is unimplemented Error launching process, description 'The operation couldn’t be completed. (OSStatus error -10814.)', reason '' The code I am using to present the view is: struct MyWebView: UIViewRepresentable { let content: String func makeUIView(context: Context) -> WKWebView { // Javascript that disables pinch-to-zoom by inserting the HTML viewport meta tag into <head> let source: String = """ var meta = document.createElement('meta'); meta.name = 'viewport'; meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'; var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = '*:focus{outline:none}body{margin:0;padding:0}'; var head = document.getElementsByTagName('head')[0]; head.appendChild(meta); head.appendChild(style); """ let script: WKUserScript = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true) let userContentController: WKUserContentController = WKUserContentController() let conf = WKWebViewConfiguration() conf.userContentController = userContentController userContentController.addUserScript(script) let webView = WKWebView(frame: CGRect.zero /*CGRect(x: 0, y: 0, width: 1000, height: 1000)*/, configuration: conf) webView.isOpaque = false webView.backgroundColor = UIColor.clear webView.scrollView.backgroundColor = UIColor.clear webView.scrollView.isScrollEnabled = false webView.scrollView.isMultipleTouchEnabled = false if #available(iOS 16.4, *) { webView.isInspectable = true } return webView } func updateUIView(_ webView: WKWebView, context: Context) { webView.loadHTMLString(content, baseURL: nil) } } This has been working for ages and ages (back to at least ios 15) - something changed. Maybe it is just a problem with the beta 17.5 release?
7
1
990
3w
Can't Select UITextField in UITableView
Summary Hello Apple Developers, I've made a custom UITableViewCell that includes a UITextField and UILabel. When I run the simulation the UITableViewCells pop up with the UILabel and the UITextField, but the UITextField isn't clickable so the user can't enter information. Please help me figure out the problem. Thank You! Sampson What I want: What I have: Screenshot Details: As you can see when I tap on the cell the UITextField isn't selected. I even added placeholder text to the UITextField to see if I am selecting the UITextField and the keyboard just isn't popping up, but still nothing. Relevant Code: UHTextField import UIKit class UHTextField: UITextField { override init(frame: CGRect) { super.init(frame: frame) configure() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } convenience init(placeholder: String) { self.init(frame: .zero) self.placeholder = placeholder } private func configure() { translatesAutoresizingMaskIntoConstraints = false borderStyle = .none textColor = .label tintColor = .blue textAlignment = .left font = UIFont.preferredFont(forTextStyle: .body) adjustsFontSizeToFitWidth = true minimumFontSize = 12 backgroundColor = .tertiarySystemBackground autocorrectionType = .no } } UHTableTextFieldCell import UIKit class UHTableTextFieldCell: UITableViewCell, UITextFieldDelegate { static let reuseID = "TextFieldCell" let titleLabel = UHTitleLabel(textAlignment: .center, fontSize: 16, textColor: .label) let tableTextField = UHTextField() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) configure() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func set(title: String) { titleLabel.text = title tableTextField.placeholder = "Enter " + title } private func configure() { addSubviews(titleLabel, tableTextField) let padding: CGFloat = 12 NSLayoutConstraint.activate([ titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor), titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding), titleLabel.heightAnchor.constraint(equalToConstant: 20), titleLabel.widthAnchor.constraint(equalToConstant: 80), tableTextField.centerYAnchor.constraint(equalTo: centerYAnchor), tableTextField.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 24), tableTextField.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding), tableTextField.heightAnchor.constraint(equalToConstant: 20) ]) } } LoginViewController class LoginViewController: UIViewController, UITextFieldDelegate { let tableView = UITableView() let loginTableTitle = ["Username", "Password"] override func viewDidLoad() { super.viewDidLoad() configureTableView() updateUI() createDismissKeyboardTapGesture() } func createDismissKeyboardTapGesture() { // create the tap gesture recognizer let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing)) // add it to the view (Could also add this to an image or anything) view.addGestureRecognizer(tap) } func configureTableView() { view.addSubview(tableView) tableView.layer.borderWidth = 1 tableView.layer.borderColor = UIColor.systemBackground.cgColor tableView.layer.cornerRadius = 10 tableView.clipsToBounds = true tableView.rowHeight = 44 tableView.delegate = self tableView.dataSource = self tableView.translatesAutoresizingMaskIntoConstraints = false tableView.removeExcessCells() NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: loginTitleLabel.bottomAnchor, constant: padding), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding), tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding), tableView.heightAnchor.constraint(equalToConstant: 88) ]) tableView.register(UHTableTextFieldCell.self, forCellReuseIdentifier: UHTableTextFieldCell.reuseID) } func updateUI() { DispatchQueue.main.async { self.tableView.reloadData() self.view.bringSubviewToFront(self.tableView) } } } extension LoginViewController: UITableViewDelegate, UITableViewDataSource{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 2 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: UHTableTextFieldCell.reuseID, for: indexPath) as! UHTableTextFieldCell let titles = loginTableTitle[indexPath.row] cell.set(title: titles) cell.titleLabel.font = UIFont.systemFont(ofSize: 16, weight: .bold) cell.tableTextField.delegate = self return cell } } Again thank you all so much for your help. If you need more clarification on this let me know.
2
0
218
3w
Apple Pencil Pro Squeeze API
I wonder if an Apple engineer could confirm: will the Apple Pencil Pro squeeze functionality be detectable in the current API, or will this be a future iPadOS extension to gesture recognizers / UIKit? I’d like to start playing with the functionality if it’s detected behind an existing event though. (Long press?)
1
0
246
3w
The iOS APP is that after three minutes or more in the background, When entering the foreground, it will be restored from the back to the background page instead of restarting.
Our current status is that after three minutes or more in the background, re-opening the app is a restart. Most of the users are claiming that they were automatically redirected to the home page of our app after a certain period of inactivity in the app. I recently upgraded my Xcode version from 12.4 to 15.3. I did not experience the problem with Xcode 12.4. It is an enterprise application, and the majority of users report restart issues. It occurred at random, and the user device contained only our application, with no other app like entertainment or gaming apps. However, I notice that many other apps are running in the background for an extended period of time (such as 20 minutes or 30 minutes). When I open the app, the same page sometimes appears in the background or the app is refreshed (like, Medium) I am not sure how they do it; I follow Apple. The rules did not do anything after entering the background. Is there anything Apple could do? How can I resolve this issue? Or it is default iOS behaviour. Please provide any documentation related to this. Please help me resolve this issue. Note: iOS device Version 15 to 17 is the latest
0
0
172
3w
How to make keyboard extension available for a MDM device?
Our keyboard extension can be accessed independently in China region with native app like Notes or Safari, however the keyboard can only be opened in the app under same project in Taiwan region. I've checked some articles about how MDM managing extensions, also make sure our RequestOpenAccess option of keyboard extension info.plist also set to Yes. I'm not sure is there anything I missed, or I just need to inform client that they need to reach out their MDM manager and modify some restrictions? If keyboard supports mobile device management (MDM), it can work with managed apps. App extensions give third-party developers a way to provide functionality to other apps or even to key systems built into the operating systems Allow full access to custom keyboard in iOS
1
0
313
3w
TipUIPopoverViewController's imageSize doesn't work
Per the apple API documentation (https://developer.apple.com/documentation/tipkit/tipuipopoverviewcontroller/imagesize), imageSize is meant to control the size of the image within the tip, but it doesn't seem to be working. My code is as follows, taken from the apple docs example: tipObservationTask = tipObservationTask ?? Task { @MainActor [weak controller] in for await shouldDisplay in tip.shouldDisplayUpdates { if shouldDisplay { let popoverController = TipUIPopoverViewController(tip, sourceItem: sourceItem) popoverController.imageSize = CGSize(width: 10, height: 10) controller?.present(popoverController, animated: true) tipPopoverController = popoverController } else { if controller?.presentedViewController is TipUIPopoverViewController { controller?.dismiss(animated: true) tipPopoverController = nil } } } }
0
0
157
4w
[SwiftUI][Crash] Weird crash with FocusBrigde.hasHostingController.getter
This is a bug I have been trying to identify and fix for more than 3 weeks. This bug mostly happens in production. The stack trace doesn't help that much, because it looks like the app is crashing due to some SwiftUI internal methods. I tried everything in my power to debug and find this bug, still nothing. Due to the lack of 100% reproducibility, it made me think that this bug is either memory-pressure related or Swift concurrency related. When does it happen: This bug happens when presenting a SwiftUI modal (UIViewControllerRepresentable)[A] which in terms presents a UIHostingController modal[B] which then presents a modally a SwiftUI sheet [C] The bug happens somewhere around staying in [C] or after dismissing it. Context: The app lifecycle is SwiftUI. The main SwiftUI screen in the app body is a UIViewControllerRepresentable containing a UISplitViewController. Here is a screenshot from the crash log in Xcode. Anyone facing a similar issue?
2
0
186
4w
#available issue on visionOS
Hi, do you guys have any idea why this code block doesn't run properly on a designed iPad app running on a vision pro simulator? I'm trying to add a hovering effect to a view in UIKit but it just doesn't enter this if statement. if #available(iOS 17.0, visionOS 1.0, *) { someView.hoverStyle = .init(effect: .automatic) }
3
0
243
May ’24
*** Assertion failure in -[MYAPP.MYCollectionView _updateLayoutAttributesForExistingVisibleViewsFadingForBoundsChange:], UICollectionView.m:6218
I am getting this error, only in iOS 17 (all version) on old code that had been working since iOS 15. The error occurs whenever the collectionView need to increase it's height beyond its initial height. The collectionView is inside a tableView. All autolayout constraints are set and everything use to work fine in previous version of iOS. *** Assertion failure in -[MYAPP.MYCollectionView _updateLayoutAttributesForExistingVisibleViewsFadingForBoundsChange:], UICollectionView.m:6218
0
0
134
Apr ’24
The screen implemented with UIScrollView cannot be scrolled after upgrading to Xcode 15.0
■Confirmation (2 points) ① Question about setting Constraints for UIScrollView. Is it correct to align the display position of the top edge of ContentsView, which contains the actual displayed content, with "ContentLayoutGuide" instead of "FrameLayoutGuide"? ② Is there a difference in screen display control between Xcode 14.1 and earlier and Xcode 15.0 when multiple Constraints are set for the same screen component? ■Background of the question On the screen that implements UIScrollView, the following two constraints regarding the display position of the top edge of ContentsView were set. Match the top of "FrameLayoutGuide". Match the top of "ContentLayoutGuide". Also, the priorities of the above two Constraints were the same. In Xcode 14.1, I was able to scroll the screen without any problems. When I upgraded to Xcode 15.0, the following error message appeared and I was unable to scroll. Unable to simultaneously satisfy constraints.
0
0
196
Apr ’24
UICollectionViewListCell not resizing
Please run the following UIKit app. It uses a collection view with compositional layout (list layout) and a diffable data source. The collection view has one section with one row. The cell contains a text field that is pinned to its contentView. import UIKit class ViewController: UIViewController { var collectionView: UICollectionView! var dataSource: UICollectionViewDiffableDataSource<String, String>! override func viewDidLoad() { super.viewDidLoad() configureHierarchy() configureDataSource() } func configureHierarchy() { collectionView = .init(frame: .zero, collectionViewLayout: createLayout()) view.addSubview(collectionView) collectionView.frame = view.bounds } func createLayout() -> UICollectionViewLayout { UICollectionViewCompositionalLayout { section, layoutEnvironment in let config = UICollectionLayoutListConfiguration(appearance: .insetGrouped) return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment) } } func configureDataSource() { let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { cell, indexPath, itemIdentifier in let textField = UITextField() textField.placeholder = "Placeholder" textField.font = .systemFont(ofSize: 100) cell.contentView.addSubview(textField) textField.pinToSuperview() } dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier) } var snapshot = NSDiffableDataSourceSnapshot<String, String>() snapshot.appendSections(["main"]) snapshot.appendItems(["demo"]) dataSource.apply(snapshot, animatingDifferences: false) } } extension UIView { func pin( to object: CanBePinnedTo, top: CGFloat = 0, bottom: CGFloat = 0, leading: CGFloat = 0, trailing: CGFloat = 0 ) { self.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ self.topAnchor.constraint(equalTo: object.topAnchor, constant: top), self.bottomAnchor.constraint(equalTo: object.bottomAnchor, constant: bottom), self.leadingAnchor.constraint(equalTo: object.leadingAnchor, constant: leading), self.trailingAnchor.constraint(equalTo: object.trailingAnchor, constant: trailing), ]) } func pinToSuperview( top: CGFloat = 0, bottom: CGFloat = 0, leading: CGFloat = 0, trailing: CGFloat = 0, file: StaticString = #file, line: UInt = #line ) { guard let superview = self.superview else { print(">> \(#function) failed in file: \(String.localFilePath(from: file)), at line: \(line): could not find \(Self.self).superView.") return } self.pin(to: superview, top: top, bottom: bottom, leading: leading, trailing: trailing) } func pinToSuperview(constant c: CGFloat = 0, file: StaticString = #file, line: UInt = #line) { self.pinToSuperview(top: c, bottom: -c, leading: c, trailing: -c, file: file, line: line) } } @MainActor protocol CanBePinnedTo { var topAnchor: NSLayoutYAxisAnchor { get } var bottomAnchor: NSLayoutYAxisAnchor { get } var leadingAnchor: NSLayoutXAxisAnchor { get } var trailingAnchor: NSLayoutXAxisAnchor { get } } extension UIView: CanBePinnedTo { } extension UILayoutGuide: CanBePinnedTo { } extension String { static func localFilePath(from fullFilePath: StaticString = #file) -> Self { URL(fileURLWithPath: "\(fullFilePath)").lastPathComponent } } Unfortunately, as soon as I insert a leading view in the cell: let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { cell, indexPath, itemIdentifier in let contentView = cell.contentView let leadingView = UIView() leadingView.backgroundColor = .systemRed let textField = UITextField() textField.placeholder = "Placeholder" textField.font = .systemFont(ofSize: 100) contentView.addSubview(leadingView) contentView.addSubview(textField) leadingView.translatesAutoresizingMaskIntoConstraints = false textField.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ leadingView.centerYAnchor.constraint(equalTo: contentView.layoutMarginsGuide.centerYAnchor), leadingView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), leadingView.widthAnchor.constraint(equalTo: contentView.layoutMarginsGuide.heightAnchor), leadingView.heightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.heightAnchor), textField.topAnchor.constraint(equalTo: contentView.topAnchor), textField.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), textField.leadingAnchor.constraint(equalTo: leadingView.trailingAnchor, constant: 16), textField.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor), textField.heightAnchor.constraint(greaterThanOrEqualToConstant: 44) ]) } the cell does not self-size, and in particular it does not accomodate the text field: What would be the best way to make the cell resize automatically?
1
0
187
Apr ’24
How do you go about creating an image carousel with SwiftUI, iOS 15.0+?
I need to create a carousel component with the following requirements (sorted by relevance): Objectives Every image is 16:9 aspect ratio and resizes to fit the screen. Needs a zoom and pan functionality, possibly the same way as iOS Photos app. Needs to work both in landscape and portrait mode, with a smooth transition between orientations. When orientation changes, the image needs to be rotated to preserve the center of the image (like Photos app and hyperoslo/Lightbox) The component should only take the minimum necessary space. In most use cases, such component should have other subviews both above and below. Circularity. I would like the carousel to wrap around. What I tried: Using a TabView with .tabViewStyle(PageTabViewStyle()).indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always)) modifiers. This didn't work: rotating the interface caused the view to get stuck between pages (it looks like it's a well known [bug]).(https://stackoverflow.com/questions/72435939/swiftui-tabview-is-not-working-properly-on-orientation-change). Implementing a single page (that is, an image view) using an UIScrollView and an UIViewRepresentable, then collecting them into an HStack. Unfortunately I need to use zoomScale and contentOffset properties of the UIScrollView outside of the UIViewRepresentable itself. The net result was that .init() was invoked for every image in the carousel at every rotation, causing severe stutters and an horrible rotation animation. Implementing the whole carousel using UIKit, and an UICollectionView, whose cells were an instance of UIScrollView. The problem is, the UIScrollView needs to recompute its constraints upon rotation but a cell is an instance of UIView, so it can't respond to rotations via viewWillTransition(to:CGSize, with: any UIViewControllerTransitionCoordinator). In the UICollectionView itself, you can only access the visible cells (one at a time is visible), and cells are cached, so even after rotating, some occasionally are presented on screen with the same appearance as before the rotation (some do, some don't, in the same UICollectionView). Also when rotating, it looks like the UIScrollView of the visible cell is being recreated, making it impossible to preserve the image center (I use this subclass of UIScrollView for this purpose). And the UICollectionView is taking the full window size, not just the bare minimum necessary space. Help: With all of this in mind, what options do I realistically have? If necessary I can raise the minimum iOS version to 16.0, even though I guess it doesn't make any significative difference since SwiftUI introduced MagnifyGesture only from iOS 17.0.
2
0
338
Apr ’24