Skip to content

Instantly share code, notes, and snippets.

View denis-obukhov's full-sized avatar
📟

Denis Obukhov denis-obukhov

📟
View GitHub Profile
@denis-obukhov
denis-obukhov / EquatablePack.swift
Last active March 11, 2024 06:15
Leverage the power of Swift 5.9 brand new Parameter Packs to adopt the Equatable protocol for use in SwiftUI's .onChange(of:) based on changes to multiple parameters!
struct EquatablePack<each V: Equatable>: Equatable {
let value: (repeat each V)
init(_ value: repeat each V) {
self.value = (repeat each value)
}
static func == (lhs: Self, rhs: Self) -> Bool {
func throwIfNotEqual<T: Equatable>(_ lhs: T, _ rhs: T) throws {
guard lhs == rhs else { throw CancellationError() }
@denis-obukhov
denis-obukhov / PageView.swift
Created March 29, 2023 16:36
SwiftUI UIPageViewController wrapper with data source/lazy loading for Identifiable data
import SwiftUI
import UIKit
struct PageView<Page: View, T: Identifiable>: UIViewControllerRepresentable {
private class ModelViewController: UIHostingController<Page?> {
var id: T.ID?
init(id: T.ID?, rootView: Page?) {
self.id = id
super.init(rootView: rootView)
@denis-obukhov
denis-obukhov / GeometryReader vs AspectRatio
Last active July 7, 2022 14:09
Don't hesitate to use .aspectRatio instead of GeometryReader in calculations based on relative container size. Especially when your subview has a dynamic content size effecting parent size back. Here's an example comparing two these approaches making a bar chart
struct GeometryReaderApproach: View {
private let values: [Double] = [152, 355, 475, 78, 173, 295]
private let maxValue: Double
init() { maxValue = values.max() ?? 0 }
var body: some View {
VStack {
let data = Array(zip(values.indices, values))
// Why not use .enumerated? Well, in general it's is not necessarily the index of the paired value.
@denis-obukhov
denis-obukhov / SwiftUIMemoryLeak.swift
Last active June 9, 2024 23:52
Are strong reference retain cycles possible with SwiftUI views that are structs? Why not? Easy! Just save a view-referenced closure somewhere. It's even impossible to weakly capture self due to the value type. See this example of implementing Commands menu & shortcuts
import SwiftUI
// Use Case: collect actions to perform menu commands
//
// Issue: Storing DetailsView context ( "isTakingSnapshot = true" ) leads to a memory leak
// All view associated memory storage doesn't get deallocated even though a view itself no longer exist
// Solution: move stored context to ViewModel and capture it as a weak reference
// MARK: - App
/*
Collection view with UICollectionViewCompositionalLayout
resets section's content offset on vertical scroll
if visibleItemsInvalidationHandler is set.
Even if it's empty
*/
import UIKit