I ran into this with a more complex project, but was able to recreate with a very simple example below. I have a List with five items in it. I use a ForEach on the items with .onMove. The onMove function has a destination value.
If my list is: A, B, C, D, E, and I drag B so it is below C, I would expect to get A, C, B, D, E. Further, I would expect that destination would be 2, since I am moving B to the 3rd index, assuming that A is at index 0.
Weirdly, destination is always 3.
Conversely, if I do the opposite, and drag C so it is above B, and I get A, C, B, D, E, destination is now 1! This makes sense, since it's the second position.
So why is it that moving down the list results in what appears to be 1-based indexing, but moving it up the list seems to be 0-based indexing? How am I supposed to reliably get the correct destination value?
@State var items = ["A", "B", "C", "D", "E"]
var body: some View {
NavigationStack {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
.onMove(perform: moveItem)
}
.toolbar {
ToolbarItem {
EditButton()
}
}
}
}
private func moveItem(from source: IndexSet, to destination: Int) {
print("destination is \(destination)")
}
}
Output from above. If I drag B below C:
destination is 3
If I drag C above B:
destination is 1
Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi everyone,
I’m wondering about Core Data. When creating a private context using newBackgroundContext(), does it automatically set the parent to the view context, or is it independent?
Additionally, if I update objects in the context created by newBackgroundContext(), will the view context automatically notice the changes, and vice versa?
Lastly, are there other ways to set parent-child context relationships between contexts?
I'd appreciate it if anyone could clarify this for me.
Thanks in advance! 😊
I am building an app where tasks can be shown on multiple selected days. The user will select a day and the available tasks should be shown. I am struggling to build a view hierarchy where the visible tasks will update when added to the currently shown day.
A simplified model looks like the below:
@Model final class Day
{
private(set) var dayID: UUID = UUID()
var show: [Item]?
}
@Model final class Item
{
private(set) var itemID: UUID = UUID()
@Relationship(inverse: \Day.show) var showDays: [Day]?
}
I have a view representing a day, and I would like it to display a list of associated tasks below.
The view doesn't update if I list a day's tasks directly, e.g. List(day.show) {}
I get a compile error if I build a sub view that queries Item using the day's show array:
let show = day.show ?? []
let predicate = #Predicate<Item> { item in
show.contains(where: { $0.itemID == item.itemID }) }
I get runtime error if I flip the predicate:
let dayID = day.dayID
let predicate: Predicate<Item> = #Predicate<Item> { item in
item.showDays.flatMap { $0.contains(where: { $0.dayID == dayID }) } ?? false }
I'm at a loss of how to approach this, other that just forcing the whole view hierarchy to update every time a make a change.
Is there a recommended approach to this situation please?
I was trying to show my nextKeyboardButton based on the value of needsInputModeSwitchKey when the viewDidLoad or viewWillAppear.
nextKeyboardButton.isHidden = !self.needsInputModeSwitchKey
However, my Xcode console always showed this error when I call the key at viewDidLoad, viewWillAppear, viewWillLayoutSubviews and viewDidLayoutSubviews.
2024-10-15 11:50:34.306515+0800 MyKeyboard[6040:25025964] [External] -[UIInputViewController needsInputModeSwitchKey] was called before a connection was established to the host application. This will produce an inaccurate result. Please make sure to call this after your primary view controller has been initialized.
The timing of connection was established to the host application. probably not related to the life cycle of extension? What's the right way use this key without warning?
Topic:
UI Frameworks
SubTopic:
UIKit
Hello, Dear Developers
Problem Description
My team is working on functionality test in iOS18.
Basically, application functionality works as expected without any modification.
However, We found a small issue in settings application.
iOS 17
In iOS17, 3rd party OSS license string in settings application would been displayed if the license button is been clicked.
Model: iPhone SE 3
OS Version: 17.6.1
iOS 18
However, in iOS18, nothing but a blank page.
Model: iPhone SE 3
OS Version: 18.0.1
Settings.bundle
Below are files in settings.bundle.
What I've searched
Using XCode 16 makes no change
Nothing about settings.bundle in iOS 18 release note
A similar post: https://forums.developer.apple.com/forums/thread/764519
The solution in the post doesn't solve my problem.
If you know the solution, please let me know.
Best wishes.
I would like to create 5 buttons in two rows
the first row contains 3 buttons while the second 2 buttons
button D should occupy the space occupied by the two buttons at the top A and B
while as you can see from the attached image button D is as wide as the others
on visualstudio c# I know how to do it but with Xcode Swift I can't find any documentation that can help me
Topic:
UI Frameworks
SubTopic:
SwiftUI
My problem: I tap in one of the TextFields defined below and try to type. After typing a single character, the cursor disappears from the TextField and I need to tap in the field again to enter the next character.
I have the following code:
@Observable
final class Species {
. . .
public var list: [String]
. . .
}
struct SpeciesCapture: View {
@State private var species = Species()
@State var trainingList: [String] = []
var body: some View {
NavigationStack {
VStack(alignment: .leading) {
HStack {
Text("some text")
Spacer()
Button("Add") {
trainingList.append("")
}
}
List {
ForEach($trainingList, id: \.self) { $animal in
TextField("Animal", text: $animal)
.textCase(.lowercase)
}
}
}
.onAppear {
trainingList = species.list
. . .
}
. . . // etc.
}
}
}
It appears that I am continually losing focus. How do I fix this problem?
Xcode 16.0 targeting iOS 18. The behavior appears in both Preview and the Simulator. (Haven't tried it on a physical device).
I want to make a simple droplet application. I've set the document types to include com.adobe.pdf files, and I am now receiving callbacks to the app delegate's application(_:open:) callback when I drop PDFs on the app icon…
But not the first time. It doesn't matter how long I wait, or whether the app is already open—the first drop never triggers the callback. All subequent drops work as expected, and I get URLs to all the files dropped.
What might be wrong? How can I debug this?
I'm new to Swift and I got this error today. I have another SwiftUI page with the same code but the error doesn't appear there.
struct CardView: View {
var location: String
var description: String
var imageName: String
var body: some View {
VStack{
Image(imageName)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 300, height: 200) // Set a fixed height for each card
.background(Color.black.opacity(0.7))
VStack(alignment: .leading) {
Spacer()
Text(location)
.padding(15)
.font(.headline)
.fontWeight(.bold)
.frame(width:300, alignment: .init(horizontal: .leading, vertical: .center))
.foregroundColor(.black)
.bold()
.background(Color.white)
.clipShape(RoundedCorner(radius: 10, corners: [.topLeft, .topRight]))
.padding([.leading, .trailing], -14)
.padding(.bottom, 10)
.offset(y: -213)
.offset(x: 28)
Text(description)
.font(.body)
.foregroundColor(.black)
.frame(width:300, alignment: .init(horizontal: .leading, vertical: .center))
.background(Color.white)
.clipShape(RoundedCorner(radius: 10, corners: [.bottomLeft, .bottomRight]))
.padding([.leading, .trailing], 14)
.padding(.bottom, 10)
.multilineTextAlignment(.leading)
.lineLimit(nil)
.offset(y: 2)
}
.padding()
.frame(width: 200)
.fixedSize()
}
.frame(height: 350) // Ensure each card has a fixed height
.background(Color.white)
.cornerRadius(10)
.shadow(radius: 5)
.padding([.leading, .trailing], 5) // Add padding to the sides for spacing
}
}
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hi,
We are going to create a tvOS App with portrait display(HDMI screen will rotate 90 degree).
It seems there is no rotate setting in tvOS18, neither Xcode provide relative support.
As our investigation, we might need to rotate each UIKit component 90 degree by code to archive it.
Is there any better suggestion?
Thanks.
With the upcoming launch of Apple Intelligence and Writing Tools, we've been forced to migrate our text editing app to TextKit 2. As soon as we released the update, we immediately got complaints about incorrect selection behaviour, where the user would tap a word to begin editing, but the caret would be shown in an undefined location, often dozens of paragraphs below the selected content.
To reproduce:
Create a UITextView backed by a standard TextKit 2 stack and a large amount of text (50,000+ words) - see sample project below
Scroll quickly through the text view (at least 20% of the way down)
Tap once to select a position in the document.
Expected:
The caret appears at the location the user tapped, and UITextView.selectedRange is the range of the text at the location of the tap. This is the behaviour of TextKit 1 based UITextViews.
Actual:
The caret is positioned at an undefined location (often completely off screen), and the selectedRange is different to the range at the location of the tap, often by several thousand. There is no pattern to the magnitude of the discrepancy.
This incorrect behaviour occurs consistently in the sample project on the simulator, but you may need to hide the keyboard by pulling down, then repeat steps 2-3 a few times. This happens on iPhone and iPad, and on iOS 17, 18, and 18.1.
Do you have any insight into why this might be happening or how to work around this issue?
Sample code is here: https://github.com/nathantesler/textkit2-issue/tree/master
I am making an IOS application using XCode Storyboard.
I have a UICollectionViewController in the following structure
UIViewController -> UIView -> UITabBarController -> UICollectionViewController
The app loads without crashing, and UICollectionViewController.viewDidLoad() and .numberOfSections() execute correctly.
But anything that I put in
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
does not seem to execute. Once the app loads, no cells load. And any print statement in the function doesn't execute either.
All elements have their corresponding class correctly assigned in the Storyboard Identity Inspector, and the cells have the correct reusable identifier.
How can I make it work correctly?
My Collection View Controller:
class ValidCardsCollectionViewController: UICollectionViewController {
let dataSource: [String] = ["hearts", "clubs", "diamonds", "spades"]
override func viewDidLoad() {
super.viewDidLoad()
print(dataSource)
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
print("Loading \(dataSource.count) cells")
return dataSource.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = UICollectionViewCell()
if let cardCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as? CardCollectionViewCell{
cardCell.configure(with: dataSource[indexPath.row])
cell = cardCell
}
print("loading cell")
return cell
}
My UICollectionViewCell:
class CardCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var lblStr: UILabel!
func configure(with str:String){
lblStr.text = str
}
}
Storyboard layout:
Simulator:
Topic:
UI Frameworks
SubTopic:
UIKit
I'm developing LockedCameraCapture extension.
My extension can capture photo, save to system photo library and load from system photo library. That's pretty nice extension.
I want to fix interface orientation to portrait for particular screen(capture screen). But I want some other screen to landscape orientation(photo viewing screen).
So, I'm using "supportedInterfaceOrientations" property and "setNeedsUpdateOfSupportedInterfaceOrientations" method for interface orientation flexibility.
This code implies the screen only supports portrait orientation.
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
.portrait
}
And I call this code to enable orientation setting.
// UIViewController # viewDidLoad
setNeedsUpdateOfSupportedInterfaceOrientations()
My App work as expected, but my CaptureExtension doesn't work.
My extension's capture screen can rotate Landscape and that's not intended behavior.
I do not understand what is happening here.
In this example, the toolbar has three Button and one Menu inside an HStack, where the every button has a .buttonStyle(_:) set.
ToolbarItem(placement: .bottomBar) {
HStack {
Button {} label: { Label("Foo", systemImage: "square") }
.buttonStyle(.bordered)
Button {} label: { Label("Bar", systemImage: "circle") }
.buttonStyle(.borderless)
Menu {
Button {} label: { Label("One", systemImage: "figure.arms.open") }
Button {} label: { Label("Two", systemImage: "figure.2.arms.open") }
} label: { Label("Baz", systemImage: "star") }
Button {} label: { Label("Quux", systemImage: "triangle") }
.buttonStyle(.borderedProminent)
}
}
Please note: the menu has a star icon.
This causes only the menu button to appear, but with the first button's icon:
If a single button have has its styles removed, the toolbar appears as expected (in this example all button styles are removed):
This example uses the bottom bar, but also happens when using placement: .navigation, placement: .topBarLeading, placement: .topBarTrailing.
We are currently combining AutoLayout and StackView to structure our cells, and we’re using UICollectionViewCompositionalLayout to set up sections. Additionally, we’re utilizing UICollectionViewDiffableDataSource to manage the data source. On the home screen, we display cells in a multi-section layout.
In the main screen, we support infinite scrolling and use a paging method that fetches 10 items at a time from the API. As we fetch and render more data, the number of displayed cells increases. However, we’ve noticed that as the number of displayed cells grows, the UI freezes during the process of fetching and rendering more data.
We suspect that the issue lies in the configuration of UICollectionViewCompositionalLayout. When data is fetched through paging, the data is applied to the CollectionViewDiffableDataSource, and during the process of displaying it on the screen, the method that configures the UICollectionViewCompositionalLayout layout is called. The problem here is that when 10 cells are displayed on the screen and we fetch more data through paging and add 10 more cells, the layout calculation is redone for all 20 cells. In other words, the UICollectionViewCompositionalLayout layout configuration occurs a total of 20 times.
Because of this, it seems that the UI freezing issue occurs as the number of cells increases.
What steps can we take to resolve this problem? We have attached the relevant code for your reference.
private lazy var collectionView = UICollectionView(
frame: .zero,
collectionViewLayout: self.createCollectionViewLayout()
)
private func createCollectionViewLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, _ in
guard let self, self.dataSource.snapshot().sectionIdentifiers.isEmpty == false
else { return LayoutProvider.emptySection() }
private func applyRefreshSnapshot(with sections: [PlateViewSection]) {
var snapshot = self.dataSource.snapshot()
snapshot.deleteAllItems()
snapshot.appendSections(sections)
sections.forEach { section in
snapshot.appendItems(section.items, toSection: section)
}
self.dataSource.apply(snapshot, animatingDifferences: false)
}
let sectionIdentifier = self.dataSource.snapshot().sectionIdentifiers[safe: sectionIndex]
let analyticsScreen = self.payload.analyticsScreen
switch sectionIdentifier?.module {
case let .tabOutlined(_, reactor):
if self.tabOutlinedView == nil {
let tabOutlinedView = self.dependency.tabOutlinedViewFactory.create(
payload: .init(
reactor: reactor,
collectionView: self.collectionView,
analyticsScreen: analyticsScreen,
currentDisplayDepthObserver: .init { event in
guard let depth = event.element else { return }
self.currentLayoutHeaderTabDepth = depth
},
selectedTabItemObserver: .init { [weak self] event in
guard let (tab, isPrimaryTabClick) = event.element else { return }
var queryParams: [String: String]?
if isPrimaryTabClick == false {
guard let currentState = self?.reactor?.currentState else { return }
let currentSelectedQueryParams = self?.dependency.userDefaults
.dictionary(forKey: Keys.PlateParamsKeys.brandQueryParams) as? [String: String]
queryParams = currentSelectedQueryParams ?? currentState.defaultQueryParams
}
self?.scrollCollectionViewToTop()
self?.collectionView.viewWithTag(
QueryToggleModuleCell.Metric.optionsMenuViewTag
)?.removeFromSuperview()
self?.reactor?.action.onNext(.refreshWithParams(
tabParams: tab?.params,
queryParams: queryParams
))
}
)
)
self.view.addSubview(tabOutlinedView)
tabOutlinedView.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview()
}
self.view.layoutIfNeeded()
self.tabOutlinedView = tabOutlinedView
self.tabOutlinedView?.emitInitializeAction()
}
return LayoutProvider.emptySection()
case let .carouselOneRowBrand(module, _):
let section = self.dependency.layoutProvider.createLayoutSection(module: module)
section.visibleItemsInvalidationHandler = { [weak self] _, _, _ in
guard let self else { return }
self.emitFetchLikedAction(module: module, sectionIndex: sectionIndex)
}
return section
case let .queryToggle(module):
return self.dependency.layoutProvider.createLayoutSection(module: module)
case .loadingIndicator:
return LayoutProvider.loadingIndicatorSection()
case let .space(module):
return self.dependency.layoutProvider.createLayoutSection(module: module)
case let .noResult(module):
return self.dependency.layoutProvider.createLayoutSection(module: module)
case let .buttonViewAll(module):
return self.dependency.layoutProvider.createLayoutSection(module: module)
case .footer:
return LayoutProvider.footerSection()
default:
return LayoutProvider.emptySection()
}
}
return layout
}
private func applyAppendSnapshot(with sections: [PlateViewSection]) {
var snapshot = self.dataSource.snapshot()
// loadingIndicator section delete
let sectionsToDelete = snapshot.sectionIdentifiers.filter { section in
section.module == .loadingIndicator
}
snapshot.deleteSections(sectionsToDelete)
snapshot.appendSections(sections)
sections.forEach { section in
snapshot.appendItems(section.items, toSection: section)
}
self.dataSource.apply(snapshot, animatingDifferences: false)
}
Topic:
UI Frameworks
SubTopic:
UIKit
To set up iOS 18 widget tint mode without any modifications, follow the steps below.
I hope this helps many people.
You just need to add one line.
.containerBackgroundRemovable(false)
ex)
struct create_view: Widget {
let kind: String = "CalendarWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
calendar_widgetEntryView(entry: entry)
}
.supportedFamilies([.systemLarge])
.containerBackgroundRemovable(false)
.contentMarginsDisabledIfAvailable()
}
}
Before:
After :
Does anyone have any idea why withAnimation is buggy when you apply a clip shape to an image? I've attached a screenshot of what happens below. Sometimes when hovering, it looks like the item gets drawn at the wrong location for a frame or two and then is placed back into position and the animation starts. It doesn't happen when the hover state ends, only the very initial hover event. This also doesn't happen without the .clipShape. I've also tried using .mask, but the same issue occurs. Has anyone ran into this?
Steps to reproduce:
From my UINavigationController, call
setViewControllers([vc1], animated: true)
Then later call
setViewControllers([vc2], animated: true)
Results:
In iOS 17, this behaves fine. In iOS 18, it crashes the UINavigationController.
Both log: Attempt to present * on * which is already presenting *
Workaround:
use setViewControllers(...animated: false)
We are in the process of migrating our application to SceneDelegate. We currently check in a variety of places if the app is running in the background for analytics reasons. The app may be running in the background due to location updates, notifications being received, or background refresh. With scene delegate, the app starts in the background state and only becomes active once a scene has connected. We have been unable to find a way if an app is truly running in the background or if a scene delegate is about to be launched.
Topic:
UI Frameworks
SubTopic:
UIKit
I have a Query in my View that brings in some data that I then filter with a few different computed properties. I then use those properties to present the data in a view. This is the view (simplified for clarity).
struct FMListView: View {
@Query(sort: \FMList.name) var fmLists: [FMList]
private var systemTodoLists: [FMList] {
fmLists.filter { $0.ownership == Ownership.system }
}
private var userTodoLists: [FMList] {
fmLists.filter { $0.ownership == Ownership.user && $0.parentList == nil}
}
private var favoriteTodoLists: [FMList] {
fmLists.filter { $0.isFavorite }
}
var body: some View {
NavigationStack {
List {
// MARK: -- System TodoLists
ForEach(systemTodoLists) { list in
NavigationLink(value: list) {
Text(list.name)
}
}
Section(header: Text("Favorites").padding(.top, -24)) {
ForEach(favoriteTodoLists) { list in
NavigationLink(value: list) {
Text(list.name)
}
}
}
// MARK: -- User TodoLists
Section(header: Text("Lists").padding(.top, -24)) {
ForEach(fmLists.filter { $0.ownership == Ownership.user && $0.parentList == nil}) { list in
NavigationLink(value: list) {
Text(list.name)
}
}
}
}
.navigationDestination(for: FMList.self) { list in
Text(list.name)
// MARK: -- ERROR HERE
Toggle(isOn: list.isFavorite) {
Label("Favorite", systemImage: IconConstants.starIcon)
}
}
}
}
}
The challenge I have here is that I need to represent the queried data in multiple different formats (for the example, I'm just using a Text view). When I navigate to the navigationDestination I want to edit the state on a property within the model but the model in this scope isn't bindable so I can't pass it into the Toggle.
I'm not sure if the issue is my use of computed properties, or if it's my not understanding binding misusing the Toggle. I'd appreciate some guidance on this use-case - allowing me to pass a bindable version of the model down the stack once filtered on the app side.
On a somewhat related note - I am filtering with computed properties because I can't do this filter within the Query predicate. The following gave me a compiler error because the ownership.user wasn't a constant value.
$0.ownership == Ownership.user
This is what the enum looks like:
enum Ownership: String, CaseIterable, Codable {
case system = "SYSTEM"
case user = "USER"
}