• Stars
    star
    1,944
  • Rank 23,845 (Top 0.5 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 3 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A library to present popovers. Simple, modern, and highly customizable. Not boring!

Header Image

Popovers

A library to present popovers.

  • Present any view above your app's main content.
  • Attach to source views or use picture-in-picture positioning.
  • Display multiple popovers at the same time with smooth transitions.
  • Supports SwiftUI, UIKit, and multitasking windows on iPadOS.
  • Highly customizable API that's super simple β€” just add .popover.
  • Drop-in replacement for iOS 14's Menu that works on iOS 13.
  • SwiftUI-based core for a lightweight structure. 0 dependencies.
  • It's 2023 β€” about time that popovers got interesting!

Showroom

Alert Color Menu Tip Standard
Alert Color Menu Tip Standard
Tutorial Picture-in-Picture Notification
Tutorial Picture in Picture Notification

Example

Includes ~20 popover examples. Download

Example app

Installation

Requires iOS 13+. Popovers can be installed through the Swift Package Manager (recommended) or Cocoapods.

Swift Package Manager
Add the Package URL:
Cocoapods
Add this to your Podfile:

https://github.com/aheze/Popovers

pod 'Popovers'

Usage

To present a popover in SwiftUI, use the .popover(present:attributes:view) modifier. By default, the popover uses its parent view as the source frame.

import SwiftUI
import Popovers

struct ContentView: View {
    @State var present = false
    
    var body: some View {
        Button("Present popover!") {
            present = true
        }
        .popover(present: $present) { /// here!
            Text("Hi, I'm a popover.")
                .padding()
                .foregroundColor(.white)
                .background(.blue)
                .cornerRadius(16)
        }
    }
}

In UIKit, create a Popover instance, then present with UIViewController.present(_:). You should also set the source frame.

import SwiftUI
import Popovers

class ViewController: UIViewController {
    @IBOutlet weak var button: UIButton!
    @IBAction func buttonPressed(_ sender: Any) {
        var popover = Popover { PopoverView() }
        popover.attributes.sourceFrame = { [weak button] in
            button.windowFrame()
        }
        
        present(popover) /// here!
    }
}

struct PopoverView: View {
    var body: some View {
        Text("Hi, I'm a popover.")
            .padding()
            .foregroundColor(.white)
            .background(.blue)
            .cornerRadius(16)
    }
}

Button 'Present popover!' with a popover underneath.


Customization

πŸ”–β€„ πŸ’ β€„ β¬œβ€„ πŸ”²β€„ ⏹  πŸŸ©β€„ πŸŸ₯  πŸŽΎβ€„ πŸ›‘β€„ πŸ‘“β€„ πŸ‘‰β€„ πŸŽˆβ€„ πŸ”°β€„

Customize popovers through the Attributes struct. Pretty much everything is customizable, including positioning, animations, and dismissal behavior.

SwiftUI
Configure in the attributes parameter.
UIKit
Modify the attributes property.

.popover(
    present: $present,
    attributes: {
        $0.position = .absolute(
            originAnchor: .bottom,
            popoverAnchor: .topLeft
        )
    }
) {
    Text("Hi, I'm a popover.")
}

var popover = Popover {
    Text("Hi, I'm a popover.")
}

popover.attributes.position = .absolute(
    originAnchor: .bottom,
    popoverAnchor: .topLeft
)

present(popover)

πŸ”–β€„Tag β€’ AnyHashable?

Tag popovers to access them later from anywhere. This is useful for updating existing popovers.

/// Set the tag.
$0.tag = "Your Tag"

/// Access it later.
let popover = popover(tagged: "Your Tag") /// Where `self` is a `UIView` or `UIViewController`.

/// If inside a SwiftUI View, use a `WindowReader`:
WindowReader { window in
    let popover = window.popover(tagged: "Your Tag")
}

Note: When you use the .popover(selection:tag:attributes:view:) modifier, this tag is automatically set to what you provide in the parameter.

πŸ’ β€„Position β€’ Position

The popover's position can either be .absolute (attached to a view) or .relative (picture-in-picture). The enum's associated value additionally configures which sides and corners are used.

  • Anchors represent sides and corners.
  • For .absolute, provide the origin anchor and popover anchor.
  • For .relative, provide the popover anchors. If there's multiple, the user will be able to drag between them like a PIP.
Anchor Reference .absolute(originAnchor: .bottom, popoverAnchor: .topLeft) .relative(popoverAnchors: [.right])

β¬œβ€„Source Frame β€’ (() -> CGRect)

This is the frame that the popover attaches to or is placed within, depending on its position. This must be in global window coordinates. Because frames are can change so often, this property is a closure. Whenever the device rotates or some other bounds update happens, the closure will be called.

SwiftUI
By default, the source frame is automatically set to the parent view. Setting this will override it.
UIKit
It's highly recommended to provide a source frame, otherwise the popover will appear in the top-left of the screen.

$0.sourceFrame = {
    /** some CGRect here */
}

 /// use `weak` to prevent a retain cycle
attributes.sourceFrame = { [weak button] in
    button.windowFrame()
}

πŸ”²β€„Source Frame Inset β€’ UIEdgeInsets

Edge insets to apply to the source frame. Positive values inset the frame, negative values expand it.

Absolute Relative
Source view has padding around it, so the popover is offset down. Source view is inset, so the popover is brought more towards the center of the screen.

⏹ Screen Edge Padding β€’ UIEdgeInsets

Global insets for all popovers to prevent them from overflowing off the screen. Kind of like a safe area. Default value is UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16).

πŸŸ©β€„Presentation β€’ Presentation

This property stores the animation and transition that's applied when the popover appears.

/// Default values:
$0.presentation.animation = .easeInOut
$0.presentation.transition = .opacity

πŸŸ₯ Dismissal β€’ Dismissal

This property stores the popover's dismissal behavior. There's a couple sub-properties here.

/// Same thing as `Presentation`.
$0.dismissal.animation = .easeInOut
$0.dismissal.transition = .opacity

/// Advanced stuff! Here's their default values:
$0.dismissal.mode = .tapOutside
$0.dismissal.tapOutsideIncludesOtherPopovers = false
$0.dismissal.excludedFrames = { [] }
$0.dismissal.dragMovesPopoverOffScreen = true
$0.dismissal.dragDismissalProximity = CGFloat(0.25)

Mode: Configure how the popover should auto-dismiss. You can have multiple at the same time!

  • .tapOutside - dismiss the popover when the user taps outside it.
  • .dragDown - dismiss the popover when the user drags it down.
  • .dragUp - dismiss the popover when the user drags it up.
  • .none - don't automatically dismiss the popover.

Tap Outside Includes Other Popovers: Only applies when mode is .tapOutside. If this is enabled, the popover will be dismissed when the user taps outside, even when another presented popover is what's tapped. Normally when you tap another popover that's presented, the current one will not dismiss.

Excluded Frames: Only applies when mode is .tapOutside. When the user taps outside the popover, but the tap lands on one of these frames, the popover will stay presented. If you want multiple popovers, you should set the source frames of your other popovers as the excluded frames.

/// Set one popover's source frame as the other's excluded frame.
/// This prevents the the current popover from being dismissed before animating to the other one.

let popover1 = Popover { Text("Hello") }
popover1.attributes.sourceFrame = { [weak button1] in button1.windowFrame() }
popover1.attributes.dismissal.excludedFrames = { [weak button2] in [ button2.windowFrame() ] }

let popover2 = Popover { Text("Hello") }
popover2.attributes.sourceFrame = { [weak button2] in button2.windowFrame() }
popover2.attributes.dismissal.excludedFrames = { [weak button1] in [ button1.windowFrame() ] }

Drag Moves Popover Off Screen: Only applies when mode is .dragDown or .dragUp. If this is enabled, the popover will continue moving off the screen after the user drags.

Drag Dismissal Proximity: Only applies when mode is .dragDown or .dragUp. Represents the point on the screen that the drag must reach in order to auto-dismiss. This property is multiplied by the screen's height.

Diagram with the top 25% of the screen highlighted in blue.

πŸŽΎβ€„Rubber Banding Mode β€’ RubberBandingMode

Configures which axes the popover can "rubber-band" on when dragged. The default is [.xAxis, .yAxis].

  • .xAxis - enable rubber banding on the x-axis.
  • .yAxis - enable rubber banding on the y-axis.
  • .none - disable rubber banding.

πŸ›‘β€„Blocks Background Touches β€’ Bool

Set this to true to prevent underlying views from being pressed.

Popover overlaid over some buttons. Tapping on the buttons has no effect.

πŸ‘“β€„Accessibility β€’ Accessibility β€’ v1.2.0

Popovers is fully accessible! The Accessibility struct provides additional options for how VoiceOver should read out content.

/// Default values:
$0.accessibility.shiftFocus = true
$0.accessibility.dismissButtonLabel = defaultDismissButtonLabel /// An X icon wrapped in `AnyView?`

Shift Focus: If enabled, VoiceOver will focus the popover as soon as it's presented.

Dismiss Button Label: A button next to the popover that appears when VoiceOver is on. By default, this is an X circle.

VoiceOver highlights the popover, which has a X button next to id.

Tip: You can also use the accessibility escape gesture (a 2-fingered Z-shape swipe) to dismiss all popovers.

πŸ‘‰β€„On Tap Outside β€’ (() -> Void)?

A closure that's called whenever the user taps outside the popover.

πŸŽˆβ€„On Dismiss β€’ (() -> Void)?

A closure that's called when the popover is dismissed.

πŸ”°β€„On Context Change β€’ ((Context) -> Void)?

A closure that's called whenever the context changed. The context contains the popover's attributes, current frame, and other visible traits.


Utilities

πŸ“˜β€„ πŸ§©β€„ πŸŒƒβ€„ πŸ“–β€„ πŸ·β€„ πŸ“„β€„

Popovers comes with some features to make your life easier.

πŸ“˜β€„Menus

New in v1.3.0! The template Menu looks and behaves pretty much exactly like the system menu, but also works on iOS 13. It's also extremely customizable with support for manual presentation and custom views.

The system menu and Popovers' custom menu, side by side
SwiftUI (Basic)
struct ContentView: View {
    var body: some View {
        Templates.Menu {
            Templates.MenuButton(title: "Button 1", systemImage: "1.circle.fill") { print("Button 1 pressed") }
            Templates.MenuButton(title: "Button 2", systemImage: "2.circle.fill") { print("Button 2 pressed") }
        } label: { fade in
            Text("Present Menu!")
                .opacity(fade ? 0.5 : 1)
        }
    }
}
SwiftUI (Customized)
Templates.Menu(
    configuration: {
        $0.width = 360
        $0.backgroundColor = .blue.opacity(0.2)
    }
) {
    Text("Hi, I'm a menu!")
        .padding()

    Templates.MenuDivider()

    Templates.MenuItem {
        print("Item tapped")
    } label: { fade in
        Color.clear.overlay(
            AsyncImage(url: URL(string: "https://getfind.app/image.png")) {
                $0.resizable().aspectRatio(contentMode: .fill)
            } placeholder: {
                Color.clear
            }
        )
        .frame(height: 180)
        .clipped()
        .opacity(fade ? 0.5 : 1)
    }

} label: { fade in
    Text("Present Menu!")
        .opacity(fade ? 0.5 : 1)
}
SwiftUI (Manual Presentation)
struct ContentView: View {
    @State var present = false
    var body: some View {
        VStack {
            Toggle("Activate", isOn: $present)
                .padding()
                .background(.regularMaterial)
                .cornerRadius(12)
                .padding()
            
            Templates.Menu(present: $present) {
                Templates.MenuButton(title: "Button 1", systemImage: "1.circle.fill") { print("Button 1 pressed") }
                Templates.MenuButton(title: "Button 2", systemImage: "2.circle.fill") { print("Button 2 pressed") }
            } label: { fade in
                Text("Present Menu!")
                    .opacity(fade ? 0.5 : 1)
            }
        }
    }
}
UIKit (Basic)
class ViewController: UIViewController {
    @IBOutlet var label: UILabel!

    lazy var menu = Templates.UIKitMenu(sourceView: label) {
        Templates.MenuButton(title: "Button 1", systemImage: "1.circle.fill") { print("Button 1 pressed") }
        Templates.MenuButton(title: "Button 2", systemImage: "2.circle.fill") { print("Button 2 pressed") }
    } fadeLabel: { [weak self] fade in
        self?.label.alpha = fade ? 0.5 : 1
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        _ = menu /// Create the menu.
    }
}
UIKit (Customized)
class ViewController: UIViewController {
    @IBOutlet var label: UILabel!

    lazy var menu = Templates.UIKitMenu(
        sourceView: label,
        configuration: {
            $0.width = 360
            $0.backgroundColor = .blue.opacity(0.2)
        }
    ) {
        Text("Hi, I'm a menu!")
            .padding()

        Templates.MenuDivider()

        Templates.MenuItem {
            print("Item tapped")
        } label: { fade in
            Color.clear.overlay(
                AsyncImage(url: URL(string: "https://getfind.app/image.png")) {
                    $0.resizable().aspectRatio(contentMode: .fill)
                } placeholder: {
                    Color.clear
                }
            )
            .frame(height: 180)
            .clipped()
            .opacity(fade ? 0.5 : 1)
        }
    } fadeLabel: { [weak self] fade in
        UIView.animate(withDuration: 0.15) {
            self?.label.alpha = fade ? 0.5 : 1
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        _ = menu /// Create the menu.
    }
}
UIKit (Manual Presentation)
class ViewController: UIViewController {
    /// ...

    @IBAction func switchPressed(_ sender: UISwitch) {
        if menu.isPresented {
            menu.dismiss()
        } else {
            menu.present()
        }
    }
}
Basic Customized Manual Presentation
Menu with 2 buttons Menu with image and divider Manually activate the menu with a toggle switch

πŸ§©β€„Animating Between Popovers

As long as the view structure is the same, you can smoothly transition from one popover to another.

SwiftUI
Use the .popover(selection:tag:attributes:view:) modifier.
UIKit
Get the existing popover using UIResponder.popover(tagged:), then call UIResponder.replace(_:with:).

struct ContentView: View {
    @State var selection: String?
    
    var body: some View {
        HStack {
            Button("Present First Popover") { selection = "1" }
            .popover(selection: $selection, tag: "1") {

                /// Will be presented when selection == "1".
                Text("Hi, I'm a popover.")
                    .background(.blue)
            }
            
            Button("Present Second Popover") { selection = "2" }
            .popover(selection: $selection, tag: "2") {

                /// Will be presented when selection == "2".
                Text("Hi, I'm a popover.")
                    .background(.green)
            }
        }
    }
}

@IBAction func button1Pressed(_ sender: Any) {
    var newPopover = Popover { Text("Hi, I'm a popover.").background(.blue) }
    newPopover.attributes.sourceFrame = { [weak button1] in button1.windowFrame() }
    newPopover.attributes.dismissal.excludedFrames = { [weak button2] in [button2.windowFrame()] }
    newPopover.attributes.tag = "Popover 1"
    
    if let oldPopover = popover(tagged: "Popover 2") {
        replace(oldPopover, with: newPopover)
    } else {
        present(newPopover) /// Present if the old popover doesn't exist.
    }
}
@IBAction func button2Pressed(_ sender: Any) {
    var newPopover = Popover { Text("Hi, I'm a popover.").background(.green) }
    newPopover.attributes.sourceFrame = { [weak button2] in button2.windowFrame() }
    newPopover.attributes.dismissal.excludedFrames = { [weak button1] in [button1.windowFrame()] }
    newPopover.attributes.tag = "Popover 2"
    
    if let oldPopover = popover(tagged: "Popover 1") {
        replace(oldPopover, with: newPopover)
    } else {
        present(newPopover)
    }
}
Smooth transition between popovers (from blue to green and back.

πŸŒƒβ€„Background

You can put anything in a popover's background.

SwiftUI
Use the .popover(present:attributes:view:background:) modifier.
UIKit
Use the Popover(attributes:view:background:) initializer.

.popover(present: $present) {
    PopoverView()
} background: { /// here!
    Color.green.opacity(0.5)
}

var popover = Popover {
    PopoverView()
} background: { /// here!
    Color.green.opacity(0.5)
}

Green background over the entire screen, but underneath the popover

πŸ“–β€„Popover Reader

This reads the popover's context, which contains its frame, window, attributes, and various other properties. It's kind of like GeometryReader, but cooler. You can put it in the popover's view or its background.

.popover(present: $present) {
    PopoverView()
} background: {
    PopoverReader { context in
        Path {
            $0.move(to: context.frame.point(at: .bottom))
            $0.addLine(to: context.windowBounds.point(at: .bottom))
        }
        .stroke(Color.blue, lineWidth: 4)
    }
}
Line connects the bottom of the popover with the bottom of the screen

πŸ·β€„Frame Tags

Popovers includes a mechanism for tagging and reading SwiftUI view frames. You can use this to provide a popover's sourceFrame or excludedFrames. Also works great when combined with PopoverReader, for connecting lines with anchor views.

Text("This is a view")
    .frameTag("Your Tag Name") /// Adds a tag inside the window.

/// ...

WindowReader { window in
    Text("Click me!")
    .popover(
        present: $present,
        attributes: {
            $0.sourceFrame = window.frameTagged("Your Tag Name") /// Retrieves a tag from the window.
        }
    )
}

πŸ“„β€„Templates

Get started quickly with some templates. All of them are inside Templates with example usage in the example app.

  • AlertButtonStyle - a button style resembling a system alert.
  • VisualEffectView - lets you use UIKit blurs in SwiftUI.
  • Container - a wrapper view for the BackgroundWithArrow shape.
  • Shadow - an easier way to apply shadows.
  • BackgroundWithArrow - a shape with an arrow that looks like the system popover.
  • CurveConnector - an animatable shape with endpoints that you can set.
  • Menu - the system menu, but built from scratch.

Notes

State Re-Rendering

If you directly pass a variable down to the popover's view, it might not update. Instead, move the view into its own struct and pass down a Binding.

Yes
The popover's view is in a separate struct, with $string passed down.
No
The button is directly inside the view parameter and receives string.

struct ContentView: View {
    @State var present = false
    @State var string = "Hello, I'm a popover."

    var body: some View {
        Button("Present popover!") { present = true }
        .popover(present: $present) {
            PopoverView(string: $string) /// Pass down a Binding ($).
        }
    }
}

/// Create a separate view to ensure that the button updates.
struct PopoverView: View {
    @Binding var string: String

    var body: some View {
        Button(string) { string = "The string changed." }
        .background(.mint)
        .cornerRadius(16)
    }
}

struct ContentView: View {
    @State var present = false
    @State var string = "Hello, I'm a popover."

    var body: some View {
        Button("Present popover!") {
            present = true
        }
        .popover(present: $present) {

            /// Directly passing down the variable (without $) is unsupported.
            /// The button might not update.
            Button(string) { 
                string = "The string changed."
            }
            .background(.mint)
            .cornerRadius(16)
        }
    }
}

Supporting Multiple Screens β€’ v1.1.0

Popovers comes with built-in support for multiple screens, but retrieving frame tags requires a reference to the hosting window. You can get this via WindowReader or PopoverReader's context.

WindowReader { window in 

}

/// If inside a popover's `view` or `background`, use `PopoverReader` instead.
PopoverReader { context in
    let window = context.window
}

Popover Hierarchy

Manage a popover's z-axis level by attaching .zIndex(_:) to its view. A higher index will bring it forwards.

Community

Author Contributing Need Help?
Popovers is made by aheze. All contributions are welcome. Just fork the repo, then make a pull request. Open an issue or join the Discord server. You can also ping me on Twitter. Or read the source code β€” there's lots of comments.

Apps Using Popovers

Find is an app that lets you find text in real life. Popovers is used for the quick tips and as a replacements for menus β€” download to check it out!

Find App

AnyTracker is an app that tracks numbers, text and prices on websites. It uses Popovers to display sleek dialogs with a nice background blur.

AnyTracker

Β 

Track Attack! is the ultimate vehicle enthusiast app for the road and track. Modern UI. Dual front & rear cameras. GPS & sensor data. RPM and throttle. Heart rate with Apple Watch. Lap timing for the track & multi-waypoint routing for the road. Easily export and share video with data overlays. Track Attack uses Popovers to display the onboarding tutorial and in-app notifications.

TrackAttack

Β 

If you have an app that uses Popovers, just make a PR or message me.

License

MIT License

Copyright (c) 2023 A. Zheng

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Stats

More Repositories

1

Setting

Compose beautiful preference panels.
Swift
1,429
star
2

SwipeActions

Add customizable swipe actions to any view.
Swift
1,016
star
3

OpenFind

An app to find text in real life.
Swift
983
star
4

Prism

A lightweight 3D renderer for SwiftUI.
Swift
923
star
5

Multiliner

An Xcode source extension to expand lengthy lines.
Swift
829
star
6

Squirrel

Enable scrolling in the Xcode Simulator!
Swift
620
star
7

VariableBlurView

A one-file, App Store upload-safe version of Janum Trivedi's variable blur effect.
Swift
372
star
8

SwiftUICraft

Minecraft but made with SwiftUI.
Swift
303
star
9

RealityKitLaunchScreen

A launch screen made with SwiftUI and RealityKit. Used in the Find app.
Swift
239
star
10

SplitSheet

A lightweight, fully interactive split-screen sheet.
Swift
188
star
11

SupportDocs

Generate help centers for your iOS apps. Hosted by GitHub and always up-to-date.
Swift
161
star
12

ProgressGif

Add progress bars to gifs!
Swift
105
star
13

AppClipQuickStart

Make an app clip.
Swift
50
star
14

implicit-curves

Graph implicit curves in Swift
Swift
30
star
15

AccessibleReality

[Accepted] My WWDC21 Swift Student Challenge submission.
Swift
26
star
16

AlternativeARKit

A custom alternative to ARKit
Swift
22
star
17

PathMapper

Find the shortest distance between 2 points on a map!
Swift
19
star
18

StitchCounter

An app to count stitches in crochet patterns. Multiplatform.
Swift
19
star
19

MarkdownTextEditor

Markdown editor in SwiftUI 3
Swift
18
star
20

CustomSCNGeometry

How to make a custom SCNGeometry
Swift
17
star
21

ClassX

Swift
15
star
22

Missions

Missions for Midnight
Swift
13
star
23

QuickOCR

Drag-and-drop to find text. A work in progress.
Swift
12
star
24

ConnectFour

Literally just Connect 4 in SwiftUI
Swift
10
star
25

AnimateBetweenCollectionLayouts

Animate from "horizontal film strip" to "Vertical/expanded stack"
Swift
10
star
26

aheze

8
star
27

SixDegrees

Swift
8
star
28

MetalGradient

Swift
8
star
29

Projector

Forget the Simulator β€” swap your aspect ratio instead!
Swift
7
star
30

Buildings

Building block app made with SwiftUI
Swift
6
star
31

CalHacks

Swift
5
star
32

AcuteCalculator

Support files for Acute Calculator
5
star
33

ARKitCube

Demo app to render a cube with ARKit and SceneKit.
Swift
5
star
34

BarcodeScanner

Swift
3
star
35

ProjectorExample

Test out your app on different device sizes
Swift
3
star
36

VidQuery

CodeDay - Most Likely to Use winner
Swift
3
star
37

OpenFind-SupportDocs

Help center and other resources for Find app
Swift
3
star
38

TableViewReverseAnimation

Swift
3
star
39

PhotoLoadingTest

Swift
3
star
40

FastAssets

Testing to get photo assets quickly
Swift
2
star
41

SampleApp

A sample app for all purposes. Tap the squircle to spin it.
Swift
2
star
42

Find-Lists

Website for lists sharing in Find (getfind.app)
JavaScript
2
star
43

MyAppClipWebsite

Swift
2
star
44

StatusBarHide

Swift
2
star
45

Findbot

A Python bot. Check it out at https://getfind.app/discord
Python
2
star
46

DeveloperAssets

Just some images. Feel free to store yours here if you want.
Rich Text Format
2
star
47

FindHelp

Short tutorials/help docs for Find app. This is outdated, but I haven't gotten around to updating it
HTML
2
star
48

MyHelpCenter

Swift
2
star
49

FindAppSupport

Useful files that may be used in Find app
HTML
2
star
50

CardScanner

Swift
2
star
51

tdee-calculator

TypeScript
2
star
52

PlaygroundSync

Git for swift playgrounds, under development
Swift
1
star
53

Licenses

For hosting the licenses I use
1
star
54

schedules-ios

The iOS companion for Schedules
Swift
1
star
55

Assets

Just some file storage.
1
star
56

ReadmeTables

Tables don't need to be boring
1
star
57

LearningWebDev

Just learning web development, and following some tutorials! Testing on GitHub Pages.
HTML
1
star
58

SwiftUIImageTransitionTest

Swift
1
star
59

PhotoLoading

Memory issue
Swift
1
star
60

SwiftUISearchBar

Swift
1
star
61

SwiftUICamera

Swift
1
star
62

Find-Assets

Screenshots and other stuff for Find (getfind.app)
1
star
63

Medium

Support files for my Medium articles, but larger projects may have their own repository.
HTML
1
star
64

Find-Issues

Bug tracker for Find
1
star
65

TwoSetTableView

Swift
1
star
66

TargetContentOffsetTest

Demo repo for my question "UICollectionViewFlowLayout `targetContentOffset` - how to keep cells centered after orientation change?"
Swift
1
star
67

Find-Website-Old

Official website of Find app
CSS
1
star
68

TextFieldConstraintDemo

Demo repo for https://stackoverflow.com/questions/65928019/how-to-reduce-width-between-ui-components-in-my-case-uilabel-and-uitextfield
Swift
1
star