Skip to content

Instantly share code, notes, and snippets.

View ryanashcraft's full-sized avatar

Ryan Ashcraft ryanashcraft

View GitHub Profile
@ryanashcraft
ryanashcraft / Makefile
Created August 1, 2025 14:03
Xcode Project Makefile
# Xcode Project Makefile
# Example of building Xcode projects with proper settings and clean output
.DEFAULT_GOAL := help
# Configuration
PROJECT_PATH = MyApp/MyApp.xcodeproj
SCHEME = MyApp
CONFIG = Debug
DERIVED_DATA = build
@ryanashcraft
ryanashcraft / xcodebuild-filter
Created July 31, 2025 18:20
xcodebuild-filter
#!/bin/bash
# xcodebuild-filter - Simplify xcodebuild output
# Usage: xcodebuild [args] | ./bin/xcodebuild-filter [--errors-only]
# Configuration
ERRORS_ONLY=false
# Parse arguments
for arg in "$@"; do
@ryanashcraft
ryanashcraft / ContentMarginsFollowReadableWidth.swift
Created July 18, 2024 15:13
iOS 17-compatible SwiftUI implementation of cellLayoutMarginsFollowReadableWidth
import SwiftUI
@available(watchOS 10.0, *)
@available(iOS 17.0, *)
private struct ContentMarginsFollowReadableWidthModifier: ViewModifier {
static let readableWidth: Double = 672
static let minInsetLength: Double = 16
@ViewBuilder
func body(content: Content) -> some View {
@ryanashcraft
ryanashcraft / sample-view-model-init.swift
Created May 19, 2024 22:44 — forked from christianselig/sample-view-model-init.swift
manually managing special view's ID
import SwiftUI
@main
struct WindowPresentationFunApp: App {
@State var appState = AppState()
var body: some Scene {
WindowGroup {
RootView(appState: appState)
.onAppear {
@ryanashcraft
ryanashcraft / SafeAreaInsetsFollowReadableWidth.swift
Last active April 18, 2024 21:01
iOS 16-compatible SwiftUI equivalent of cellLayoutMarginsFollowReadableWidth
import SwiftUI
private struct SafeAreaInsetsFollowReadableWidthModifier: ViewModifier {
static let readableWidth: Double = 672
@ViewBuilder
func body(content: Content) -> some View {
GeometryReader { geometry in
let insetLength = max(0, geometry.size.width - Self.readableWidth) / 2
@ryanashcraft
ryanashcraft / Bundle+swiftUIPreviewsCompatibleModule.swift
Created April 18, 2024 16:43
Hacky workaround to use Bundle.module with SwiftUI previews (tested with Xcode 15.3)
import Foundation
private extension Bundle {
private static let packageName = "my-package"
private static let moduleName = "MyModule"
static var swiftUIPreviewsCompatibleModule: Bundle {
final class CurrentBundleFinder {}
let isPreview = ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
import SwiftUI
// Works around a critical issue observed on iPads with a trackpad cursor.
//
// With highPriorityGesture, other interactive content on the screen can become unresponsive after
// triggering the gesture with an iPad cursor. It's as if the gesture seems to never relinquish its
// grasp on the entire UI, until you interact with the screen directly with a tap or trigger a
// context menu.
//
// This view modifier and extension works around this by forcing the gesture to be "cancelled" by
@ryanashcraft
ryanashcraft / UIKitTabView.swift
Created March 25, 2021 02:42 — forked from Amzd/UIKitTabView.swift
UIKitTabView. SwiftUI tab bar view that respects navigation stacks when tabs are switched (unlike the TabView implementation)
/// An iOS style TabView that doesn't reset it's childrens navigation stacks when tabs are switched.
public struct UIKitTabView: View {
private var viewControllers: [UIHostingController<AnyView>]
private var selectedIndex: Binding<Int>?
@State private var fallbackSelectedIndex: Int = 0
public init(selectedIndex: Binding<Int>? = nil, @TabBuilder _ views: () -> [Tab]) {
self.viewControllers = views().map {
let host = UIHostingController(rootView: $0.view)
host.tabBarItem = $0.barItem
import SwiftUI
// Models
enum Lyric {
case line(String)
case pause(TimeInterval)
}
class ScrollToModel: ObservableObject {
import SwiftUI
struct WidthPreferenceKey: PreferenceKey {
typealias Value = CGFloat?
static var defaultValue: CGFloat?
static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
if let nextValue = nextValue(), value != nextValue {
value = nextValue