I'm trying to determine if it’s possible to detect when a user interacts with a Slide Over window while my app is running in the background on iPadOS. I've explored lifecycle methods such as scenePhase and various UIApplication notifications (e.g., willResignActiveNotification) to detect focus loss, but these approaches don't seem to capture the event reliably. Has anyone found an alternative solution or workaround for detecting this specific state change? Any insights or recommended practices would be greatly appreciated.
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
I have an image in the xcassets file which is localized for different languages. When setting App language to Traditional Chinese, it always displays the Simplified Chinese image. This happens on latest iOS 18.5 system, but not on a lower system version.
The feedback assistant ID is FB17663546
I am trying to work with the data inside the barcode string in shared PKPass.
The documentation shows that is should look for @property (nonatomic, readonly, nullable) PKBarcode *primaryBarcode;
I have tried to use it like this
guard let code = pass.primaryBarcode?.message else { return }
I get a constant message that PKPass has no member primaryBarcode
The PKPass.h file in my IOS SDK does not seem to include the @property primaryBarcode or @property barcode.
I am running Xcode 16.4 (16F6) and my app target is 17.6 + Is there a restriction on this property?
I cannot find an SDK later than mine - the App Store does not offer one.
I am unsure of this is a public or private issue - does anyone know?
Thanks for reading this.
Max
Topic:
UI Frameworks
SubTopic:
SwiftUI
In keyboard shortcuts this buttons at the bottom are overlappin.
Any one
else?
Topic:
UI Frameworks
SubTopic:
General
为什么App 上传testFlight之后。无法通过NFC的方式唤醒 APP Clips。是必须要上架商店之后才能支持么?
I have a UITextView that contains paragraphs with text bullet lists (via NSTextList). I also implement NSTextContentStorageDelegate.textContentStorage(_:, textParagraphWith:) in order to apply some custom attributes to the text without affecting the underlying attributed text. My implementation returns a new NSParagraph that modifies the foreground color of the text. I based this on the example in the WWDC 21 session "Meet Text Kit 2".
UITextView stops rendering the bullets when I implement the delegate function and return a custom paragraph. Why?
func textContentStorage(_ textContentStorage: NSTextContentStorage, textParagraphWith range: NSRange) -> NSTextParagraph? {
guard let originalText = textContentStorage.textStorage?.attributedSubstring(from: range) else { return nil }
let updatedText = NSMutableAttributedString(attributedString: originalText)
updatedText.addAttribute(.foregroundColor, value: UIColor.green, range: NSRange(location: 0, length: updatedText.length))
let paragraph = NSTextParagraph(attributedString: updatedText)
// Verify that the text still contains NSTextList
if let paragraphStyle = paragraph.attributedString.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle {
assert(!paragraphStyle.textLists.isEmpty)
} else {
assertionFailure("Paragraph has lost its text lists")
}
return paragraph
}
I have the MainView as the active view if the user is logged in(authenticated). the memory allocations when we run profile is pretty good. We have graphql fetching, we have token handling eg: This is All heap:
1 All Heap & Anonymous VM 13,90 MiB 65408 308557 99,10 MiB 373965 Ratio: %0.14, %0.86
After what i have checked this is pretty good for initialise and using multiple repositories eg. But when we change tabs:
1 All Heap & Anonymous VM 24,60 MiB 124651 543832 156,17 MiB 668483 Ratio: %0.07, %0.40
And that is not pretty good. So i guess we need to "kill" it or something. How? I have tried some techniques in a forum this was a recommended way:
public struct LazyView<Content: View>: View {
private let build: () -> Content
@State private var isVisible = false
public init(_ build: @escaping () -> Content) {
self.build = build
}
public var body: some View {
build()
Group {
if isVisible {
build()
} else {
Color.clear
}
}
.onAppear { isVisible = true }
.onDisappear { isVisible = false }
}
}
But this did not help at all. So under here is the one i use now. So pleace guide me for making this work.
import DIKit
import CoreKit
import PresentationKit
import DomainKit
public struct MainView: View {
@Injected((any MainViewModelProtocol).self) private var viewModel
private var selectedTabBinding: Binding<MainTab> {
Binding(
get: { viewModel.selectedTab },
set: { viewModel.selectTab($0) }
)
}
public init() {
// No additional setup needed
}
public var body: some View {
NavigationStack(path: Binding(
get: { viewModel.navigationPath },
set: { _ in }
)) {
TabView(selection: selectedTabBinding) {
LazyView {
FeedTabView()
}
.tabItem {
Label("Feed", systemImage: "house")
}
.tag(MainTab.feed)
LazyView {
ChatTabView()
}
.tabItem {
Label("Chat", systemImage: "message")
}
.tag(MainTab.chat)
LazyView {
JobsTabView()
}
.tabItem {
Label("Jobs", systemImage: "briefcase")
}
.tag(MainTab.jobs)
LazyView {
ProfileTabView()
}
.tabItem {
Label("Profile", systemImage: "person")
}
.tag(MainTab.profile)
}
.accentColor(.primary)
.navigationDestination(for: MainNavigationDestination.self) { destination in
switch destination {
case .profile(let userId):
Text("Profile for \(userId)")
case .settings:
Text("Settings")
case .jobDetails(let id):
Text("Job details for \(id)")
case .chatThread(let id):
Text("Chat thread \(id)")
}
}
}
}
}
import SwiftUI
public struct LazyView<Content: View>: View {
private let build: () -> Content
public init(_ build: @escaping () -> Content) {
self.build = build
}
public var body: some View {
build()
}
}
Hello all,
I'm confused about how to show lanes in CarPlay.
I understand CPLaneGuidance and CPLane
I don't find anywhere where to tell Carplay which icon to show for each lane.
I've found some information saying we put the icon in CPManeuver, but then CPManeuver is linked to only one CPLaneGuidance, and we can put only one icon in CPManeuver.
At the same time, we might have multiple lanes.
Any help, tips, or examples would be highly helpful.
So I'm dealing with a really obtuse crash that appears to be a stack overflow in an internal SwiftUI code path creating a Color.Resolved. I haven't found anyone one else with this issue online, and I cannot get it to reproduce on my own device. Interestingly enough, it is only happening on 1 device in the field (according to XCode crash logs).
Here are some lines from the crashed thread. You can see that my code is never called, and it appears to be starting in some Array equality check checking the equality of colors (which I can't think of anywhere in my app I am doing anyway).
You can see from this trace here that it appears to be a recursive call through Color.Resolved and NSColor.withColorAppearance. I don't have any idea how to solve this, but it keeps happening with at least one in-the-field device across multiple app updates.
So my whole app is open source on github at https://github.com/msdrigg/roam, but I don't even use NSColor explicitly anywhere except for here which doesn't match the stack trace.
I also tried changing the accent color of the app with defaults write com.msdrigg.roam AppleAccentColor -integer 1 to see if that somehow caused the crash, but my app opened up totally fine (and respected the change). Besides this, the only places I think I could be using dynamic colors is I when define an AccentColor and a WidgetBackground color for my app using xcassets, and then I use these colors from SwiftUI. In most of my app I stick to the system colors (Color.gray and such).
Thread 0 Crashed:
0 libsystem_pthread.dylib 0x000000018601213c ___chkstk_darwin + 60
1 CoreFoundation 0x0000000186108434 -[NSArray isEqualToArray:] + 52 (NSArray.m:454)
2 AppKit 0x000000018a21fcd4 -[NSCoreUICatalogColor resolvedCUINamedColorForAppearance:] + 164 (NSColor.m:5057)
3 AppKit 0x0000000189c32cd4 -[NSCoreUICatalogColor resolvedColor] + 48 (NSColor.m:5148)
4 AppKit 0x0000000189c31e74 -[NSDynamicNamedColor colorUsingColorSpace:] + 32 (NSColor.m:4410)
5 SwiftUICore 0x0000000221ca9fd8 CoreColorPlatformColorGetComponents + 116 (CoreColorFunctions.m:149)
6 SwiftUICore 0x0000000221faaf28 specialized Color.Resolved.init(platformColor:) + 92 (CoreColor.swift:14)
7 SwiftUICore 0x0000000221faa5b0 Color.Resolved.init(platformColor:) + 16 (<compiler-generated>:0)
8 SwiftUI 0x00000001b53b1dc4 closure #1 in NSColor.resolve(in:) + 20 (AppKitColorConversions.swift:156)
9 SwiftUI 0x00000001b53b222c partial apply for closure #1 in static NSColor.withColorAppearance(in:_:) + 32 (<compiler-generated>:0)
10 SwiftUI 0x00000001b46b1e54 closure #1 in SubmitTriggerSource.dispatchUpdate(_:) + 28 (PlatformViewCoordinator.swift:12)
11 SwiftUI 0x00000001b5484488 thunk for @escaping @callee_guaranteed () -> () + 28 (<compiler-generated>:0)
12 AppKit 0x0000000189c174a4 +[NSAppearance _performWithCurrentAppearance:usingBlock:] + 72 (NSAppearance.m:2408)
13 SwiftUI 0x00000001b53b2088 specialized static NSColor.withColorAppearance(in:_:) + 324 (AppKitColorConversions.swift:142)
14 SwiftUI 0x00000001b53b1e7c protocol witness for ColorProvider.resolve(in:) in conformance NSColor + 68 (<compiler-generated>:151)
15 SwiftUICore 0x0000000222436e6c ColorBox.resolve(in:) + 124 (Color.swift:288)
16 SwiftUICore 0x0000000222435e30 Color.resolve(in:) + 72 (Color.swift:87)
17 SwiftUI 0x00000001b53b1c88 closure #1 in NSColor.init(_:) + 196 (AppKitColorConversions.swift:124)
18 SwiftUI 0x00000001b4542714 thunk for @escaping @callee_guaranteed (@guaranteed NSAppearance) -> (@owned NSColor) + 56 (<compiler-generated>:0)
19 AppKit 0x0000000189c31e74 -[NSDynamicNamedColor colorUsingColorSpace:] + 32 (NSColor.m:4410)
//// ... Repeating for 500 lines
500 SwiftUICore 0x0000000221ca9fd8 CoreColorPlatformColorGetComponents + 116 (CoreColorFunctions.m:149)
501 SwiftUICore 0x0000000221faaf28 specialized Color.Resolved.init(platformColor:) + 92 (CoreColor.swift:14)
502 SwiftUICore 0x0000000221faa5b0 Color.Resolved.init(platformColor:) + 16 (<compiler-generated>:0)
503 SwiftUI 0x00000001b53b1dc4 closure #1 in NSColor.resolve(in:) + 20 (AppKitColorConversions.swift:156)
504 SwiftUI 0x00000001b53b222c partial apply for closure #1 in static NSColor.withColorAppearance(in:_:) + 32 (<compiler-generated>:0)
505 SwiftUI 0x00000001b46b1e54 closure #1 in SubmitTriggerSource.dispatchUpdate(_:) + 28 (PlatformViewCoordinator.swift:12)
506 SwiftUI 0x00000001b5484488 thunk for @escaping @callee_guaranteed () -> () + 28 (<compiler-generated>:0)
507 AppKit 0x0000000189c174a4 +[NSAppearance _performWithCurrentAppearance:usingBlock:] + 72 (NSAppearance.m:2408)
508 SwiftUI 0x00000001b53b2088 specialized static NSColor.withColorAppearance(in:_:) + 324 (AppKitColorConversions.swift:142)
509 SwiftUI 0x00000001b53b1e7c protocol witness for ColorProvider.resolve(in:) in conformance NSColor + 68 (<compiler-generated>:151)
510 SwiftUICore 0x0000000222436e6c ColorBox.resolve(in:) + 124 (Color.swift:288)
full-log.crash
My app inputs electrical waveforms from an IV485B39 2 channel USB device using an AVAudioSession. Before attempting to acquire data I make sure the input device is available as follows:
AVAudiosSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory :AVAudioSessionCategoryRecord error:&err];
NSArray *inputs = [audioSession availableInputs];
I have been using this code for about 10 years.
My app is scriptable so a user can acquire data from the IV485B29 multiple times with various parameter settings (sampling rates and sample duration). Recently the scripts have been failing to complete and what I have notice that when it fails the list of available inputs is missing the USBAudio input. While debugging I have noticed that when working properly the list of inputs includes both the internal microphone as well as the USBAudio device as shown below.
VIB_TimeSeriesViewController:***Available inputs = (
"<AVAudioSessionPortDescription: 0x11584c7d0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Front>",
"<AVAudioSessionPortDescription: 0x11584cae0, type = USBAudio; name = 485B39 200095708064650803073200616; UID = AppleUSBAudioEngine:Digiducer.com :485B39 200095708064650803073200616:000957 200095708064650803073200616:1; selectedDataSource = (null)>"
)
But when it fails I only see the built in microphone.
VIB_TimeSeriesViewController:***Available inputs = (
"<AVAudioSessionPortDescription: 0x11584cef0, type = MicrophoneBuiltIn; name = iPad Microphone; UID = Built-In Microphone; selectedDataSource = Front>"
)
If I only see the built in microphone I immediately repeat the three lines of code and most of the "inputs" contains both the internal microphone and the USBAudioDevice
AVAudiosSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory :AVAudioSessionCategoryRecord error:&err];
NSArray *inputs = [audioSession availableInputs];
This fix always works on my M2 iPadPro and my iPhone 14 but some of my customers have older devices and even with 3 tries they still get faults about 1 in 10 tries.
I rolled back my code to a released version from about 12 months ago where I know we never had this problem and compiled it against the current libraries and the problem still exists. I assume this is a problem caused by a change in the AVAudioSession framework libraries. I need to find a way to work around the issue or get the library fixed.
I want use SensorKit data for research purposes in my current app.
I have applied for and received permission from Apple to access SensorKit Data. I have granting all the necessary permissions. But no data retrieved.
I am using didCompleteFetch for retrieving data from Sensorkit. CompleteFetch method calls but find the data. Below is my SensorKitManager Code.
import SensorKit
import Foundation
protocol SensorManagerDelegate: AnyObject {
func didFetchPhoneUsageReport(_ reports: [SRPhoneUsageReport])
func didFetchAmbientLightSensorData(_ data: [SRAmbientLightSample])
func didFailFetchingData(error: Error)
}
class SensorManager: NSObject, SRSensorReaderDelegate {
private let phoneUsageReader: SRSensorReader
private let ambientLightReader: SRSensorReader
weak var delegate: SensorManagerDelegate?
override init() {
self.phoneUsageReader = SRSensorReader(sensor: .phoneUsageReport)
self.ambientLightReader = SRSensorReader(sensor: .ambientLightSensor)
super.init()
self.phoneUsageReader.delegate = self
self.ambientLightReader.delegate = self
}
func requestAuthorization() {
let sensors: Set<SRSensor> = [.phoneUsageReport, .ambientLightSensor]
guard phoneUsageReader.authorizationStatus != .authorized || ambientLightReader.authorizationStatus != .authorized else {
log("Already authorized. Fetching data directly...")
fetchSensorData()
return
}
SRSensorReader.requestAuthorization(sensors: sensors) { [weak self] error in
DispatchQueue.main.async {
if let error = error {
self?.log("Authorization failed: \(error.localizedDescription)", isError: true)
self?.delegate?.didFailFetchingData(error: error)
} else {
self?.log("Authorization granted.")
self?.fetchSensorData()
}
}
}
}
func fetchSensorData() {
guard let fromDate = Calendar.current.date(byAdding: .day, value: -1, to: Date()) else {
log("Failed to calculate 'from' date.", isError: true)
return
}
let fromTime = SRAbsoluteTime.fromCFAbsoluteTime(_cf: fromDate.timeIntervalSinceReferenceDate)
let toTime = SRAbsoluteTime.fromCFAbsoluteTime(_cf: Date().timeIntervalSinceReferenceDate)
let phoneUsageRequest = SRFetchRequest()
phoneUsageRequest.from = fromTime
phoneUsageRequest.to = toTime
phoneUsageRequest.device = SRDevice.current
let ambientLightRequest = SRFetchRequest()
ambientLightRequest.from = fromTime
ambientLightRequest.to = toTime
ambientLightRequest.device = SRDevice.current
phoneUsageReader.fetch(phoneUsageRequest)
ambientLightReader.fetch(ambientLightRequest)
}
// ✅ Delegate Methods
func sensorReader(_ reader: SRSensorReader, didCompleteFetch fetchRequest: SRFetchRequest) {
Task.detached {
if reader.sensor == .phoneUsageReport {
if let samples = reader.fetch(fetchRequest) as? [SRPhoneUsageReport] {
DispatchQueue.main.async { [weak self] in
self?.delegate?.didFetchPhoneUsageReport(samples)
}
}
} else if reader.sensor == .ambientLightSensor {
if let samples = reader.fetch(fetchRequest) as? [SRAmbientLightSample] {
DispatchQueue.main.async { [weak self] in
self?.delegate?.didFetchAmbientLightSensorData(samples)
}
}
}
}
}
func sensorReader(_ reader: SRSensorReader, fetching fetchRequest: SRFetchRequest, didFetchResult result: SRFetchResult<AnyObject>) -> Bool {
return true
}
func sensorReader(_ reader: SRSensorReader, fetching fetchRequest: SRFetchRequest, failedWithError error: any Error) {
DispatchQueue.main.async { [weak self] in
self?.delegate?.didFailFetchingData(error: error)
}
}
// MARK: - Logging Helper
private func log(_ message: String, isError: Bool = false) {
if isError {
print("❌ [SensorManager] \(message)")
} else {
print("✅ [SensorManager] \(message)")
}
}
}
And ViewController
import UIKit
import SensorKit
class ViewController: UIViewController {
private var sensorManager: SensorManager!
override func viewDidLoad() {
super.viewDidLoad()
setupSensorManager()
}
private func setupSensorManager() {
sensorManager = SensorManager()
sensorManager.delegate = self
sensorManager.requestAuthorization()
}
}
// MARK: - SensorManagerDelegate
extension ViewController: SensorManagerDelegate {
func didFetchPhoneUsageReport(_ reports: [SRPhoneUsageReport]) {
for report in reports {
print("Total Calls: (report.totalOutgoingCalls + report.totalIncomingCalls)")
print("Outgoing Calls: (report.totalOutgoingCalls)")
print("Incoming Calls: (report.totalIncomingCalls)")
print("Total Call Duration: (report.totalPhoneCallDuration) seconds")
}
}
func didFetchAmbientLightSensorData(_ data: [SRAmbientLightSample]) {
for sample in data {
print(sample)
}
}
func didFailFetchingData(error: Error) {
print("Failed to fetch data: \(error.localizedDescription)")
}
}
Could anyone please assist me in resolving this issue? Any guidance or troubleshooting steps would be greatly appreciated.
Hi, I've got an app that displays markdown in UITextView / NSTextView.
I would like it to behave like Notes app does, that is if user types the line start modifier, e.g:
"# "
or
"> "
I would like the keyboard to show a capitalized letters.
I've tried looking into overriding insertText - and it breaks the predictive text (can not insert white space).
I've tried implementing UITextInputTokenizer but no luck either.
Like I said, I see the Notes app does it perfectly, so maybe I'm missing something obvious or there is a way to do it without interrupting the auto-correction and predictive text.
Ideally same thing can be applied to the NSTextView as the app support both platforms.
I first applied a snapshot on the main thread like this:
var snapshot = NSDiffableDataSourceSnapshot<Section, MessageViewModel>()
snapshot.appendSections([.main])
snapshot.appendItems([], toSection: .main)
dataSource.applySnapshotUsingReloadData(snapshot)
After loading data, I applied the snapshot again using:
Task { @MainActor in
await dataSource.applySnapshotUsingReloadData(snapshot)
}
On an iPhone 13 mini, I received the following warning:
Warning: applying updates in a non-thread confined manner is dangerous and can lead to deadlocks. Please always submit updates either always on the main queue or always off the main queue
However, this warning did not appear when I ran the same code on an iPhone 16 Pro simulator.
Can anyone explain it to me? Thank you
I am currently developing an AR experience using ARKit with SceneKit and am looking to implement functionality that enables:
Zooming into the AR camera feed, ideally leveraging the ultra-wide or telephoto lenses available on supported devices.
Macro-style focus capabilities, allowing users to view and interact with virtual content closely aligned with small or nearby real-world objects (within a few centimeters).
My objective is to ensure that ARKit continues to render the scene accurately while enabling a zoomed-in view or macro-level focus for better detail visibility and alignment.
Could you please advise on:
Whether ARKit currently supports camera zoom or allows access to macro or ultra-wide cameras within an ARSession.
Limitations or considerations when using multi-camera setups in conjunction with ARKit.
Any guidance or references to documentation or sample code would be greatly appreciated.
Best regards,
Ayush
I have a popover/sheet in iOS which allows users to search and add items to a list. When the sheet is shown, the search should always be active.
I am using searchable on a NavigationStack inside the sheet. I am using the isPresented parameter to activate search.
My issue is with the animation of the search activation. Even if I use...
isPresented: .constant(true)
...the search isn't activated until the sheet has completed it's entrance animation, resulting in two stages of animation.
I can't add a video here, but the two images below show the steps I am seeing. First a slide up animation, with the search in the navigation drawer, then a second animation, once the sheet is fully in place, as the search becomes active.
Is it possible to merge these two animations, so search is in place when the sheet animates up?
Topic:
UI Frameworks
SubTopic:
SwiftUI
I'm working on a macOS application that needs to query the list of available printers using NSPrinter.printerNames. For performance reasons, I'd like to perform this operation on a background thread.
However, since NSPrinter is part of AppKit, and AppKit is generally not thread-safe unless explicitly stated, I want to confirm:
Is it safe to call NSPrinter.printerNames from a background thread?
I couldn’t find explicit guidance in the documentation regarding the thread-safety of printerNames, so any clarification or best practices would be appreciated.
Thanks in advance!
Note: I tested this api on a background thread in code and it did not give any error.
Description:
When initiating the print flow via UIPrintInteractionController, and no printer is initially connected, iOS displays all possible paper sizes in the paper selection UI. However, if a printer connects in the background after this view is shown, the list of paper sizes does not automatically refresh to reflect only the options supported by the connected printer.
If the user selects an incompatible paper size (one not supported by the printer that has just connected), the app crashes due to an invalid configuration.
Steps to Reproduce:
Launch the app and navigate to the print functionality.
Tap the Print button to invoke UIPrintInteractionController.
At this point, no printer is yet connected. iOS displays all available paper sizes.
While the paper selection UI is visible, the AirPrint-compatible printer connects in the background.
Without dismissing the controller, the user selects a paper size (e.g., one that is not supported by the printer).
The app crashes.
Expected Result: App should not crash
Once the printer becomes available (connected in the background), the paper size options should refresh automatically.
The list should be filtered to only include sizes that are compatible with the connected printer.
This prevents the user from selecting an invalid option, avoiding crashes.
Actual Result: App crashes
The paper size list remains unfiltered.
The user can still select unsupported paper sizes.
Selecting an incompatible option causes the app to crash, due to a mismatch between UI selection and printer capability.
Topic:
UI Frameworks
SubTopic:
UIKit
I'm trying to switch to UIKit's document lifecycle due to serious bugs with SwiftUI's version.
However I'm noticing the template project from Xcode isn't compatible with Swift 6 (I already migrated my app to Swift 6.). To reproduce:
File -> New -> Project
Select "Document App" under iOS
Set "Interface: UIKit"
In Build Settings, change Swift Language Version to Swift 6
Run app
Tap "Create Document"
Observe: crash in _dispatch_assert_queue_fail
Does anyone know of a work around other than downgrading to Swift 5?
Hello,
I’m developing an app where I display a SwiftUI view inside a UIHostingController embedded within a UIKit ViewController.
I’m trying to animate the height of the UIHostingController’s view based on a switch’s value, but the SwiftUI view doesn’t animate at all.
Below is a simplified version of my code:
class ViewController: UIViewController {
private lazy var parentView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var hostingView: UIView = {
let testView = TestView()
let hostingController = UIHostingController(rootView: testView)
let view = hostingController.view!
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var button: UISwitch = {
let button = UISwitch()
button.addTarget(self, action: #selector(onClickSwitch(sender:)), for: .valueChanged)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private var hostingViewHeightConstraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(parentView)
parentView.addSubview(hostingView)
parentView.addSubview(button)
NSLayoutConstraint.activate([
parentView.topAnchor.constraint(equalTo: view.topAnchor),
parentView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
parentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
parentView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
NSLayoutConstraint.activate([
hostingView.bottomAnchor.constraint(equalTo: parentView.bottomAnchor),
hostingView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor),
hostingView.trailingAnchor.constraint(equalTo: parentView.trailingAnchor)
])
hostingViewHeightConstraint = hostingView.heightAnchor.constraint(equalTo: parentView.heightAnchor, multiplier: 0.5)
hostingViewHeightConstraint?.isActive = true
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: parentView.centerXAnchor),
NSLayoutConstraint(item: button,
attribute: .centerY,
relatedBy: .equal,
toItem: parentView,
attribute: .centerY,
multiplier: 0.25,
constant: 0)
])
}
@objc func onClickSwitch(sender: UISwitch) {
hostingViewHeightConstraint?.isActive = false
let multiplier: CGFloat = sender.isOn ? 0.25 : 0.5
hostingViewHeightConstraint = hostingView.heightAnchor.constraint(equalTo: parentView.heightAnchor, multiplier: multiplier)
hostingViewHeightConstraint?.isActive = true
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
}
I’m looking for the behavior demonstrated in the video below:
Does anyone have suggestions on how to achieve this?
I have a grid setup where I'm displaying multiple images which is working fine. Images are ordered by the date they're added, newest to oldest.
I'm trying to set it up so that the user can change the sort order themselves but am having trouble getting the view to update.
I'm setting the fetch request using oldest to newest as default when initialising the view, then when its appears updating the sort descriptor
struct ProjectImagesListView: View {
@Environment(\.managedObjectContext) private var viewContext
var project : Project
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
@FetchRequest var pictures: FetchedResults<Picture>
var body: some View {
ScrollView {
LazyVGrid(columns: columns) {
ForEach(pictures) { picture in
NavigationLink(destination: ProjectImageDetailView(picture: picture)) {
if let pictureData = picture.pictureThumbnailData, let uiImage = UIImage(data: pictureData) {
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
.frame(height: 100)
} else {
Image("missing")
.resizable()
.scaledToFit()
.frame(height: 100)
}
}
}
}
}
.navigationBarTitle("\(project.name ?? "") Images", displayMode: .inline)
.onAppear() {
guard let sortOrder = getSettingForPhotoOrder() else { return }
guard let sortOrderValue = sortOrder.settingValue else { return }
NSLog("sortOrderPhotos: \(String(describing: sortOrder.settingValue))")
if sortOrderValue == "Newest" {
NSLog("sortOrderPhotos: Change from default")
let newSortDescriptor = NSSortDescriptor(keyPath: \Picture.dateTaken, ascending: false)
pictures.nsSortDescriptors = [newSortDescriptor]
}
}
}
func getSettingForPhotoOrder() -> Setting? {
let fetchRequest: NSFetchRequest<Setting> = Setting.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name = %@", "photoSortOrder")
fetchRequest.fetchLimit = 1
do {
let results = try viewContext.fetch(fetchRequest)
return results.first
} catch {
print("Fetching Failed")
}
return nil
}
init(project: Project) {
self.project = project
_pictures = FetchRequest(
entity: Picture.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \Picture.dateTaken, ascending: true)
],
predicate: NSPredicate(format: "project == %@", project)
)
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI