An open source package for as-you-type formatting in SwiftUI.

Overview

DiffableTextViews

An open source package for as-you-type formatting in SwiftUI.

DiffableTextFieldXAmount.gif

DiffableTextFieldXPhone.gif

Features

Feature Description
⌨️ Responsive Formats text as you type
🪄 Automagical Binds text to a chosen data type
✔️ Proper Validates and autocorrects input
🛠️ Versatile Uses snapshots and attributes
🏃‍♂️ Performant Uses O(n) differentiation algorithms
🏝️ Standalone Uses no remote dependencies
📖 Open source 100% transparent, as it should be

Algorithms

Algorithm Description Complexity
📖 ∆Text Determines selection when text changes ≤ Linear
↔️ ∆Selection Determines selection when selection changes ≤ Linear
Attributes Determines selection based on attributes ≤ Linaer

Installation

Simple instructions on how to install this package.

Swift Package Manager

  1. Select https://github.com/oscbyspro/DiffableTextViews.
  2. Select a VERSIONED release.

Import

import DiffableTextViews

Requirements

Swift iOS iPadOS Mac Catalyst tvOS
5.0+ 15.0+ 15.0+ 15.0+ 15.0+

Examples

The example app provides quick-and-easy-to-use customization tools.

Number Pattern
Number Pattern

Installation

Download this package and compile/run it with Xcode.

Views

DiffableTextField

A text field that binds values and formats them as you type.

Features

Feature Description
📱 SwiftUI Value, style, done
⛰️ Environment Uses environment values
🔎 Focus Supports SwiftUI.FocusState

Environment

environment(\.locale, _:)
environment(\.layoutDirection, _:)
diffableTextViews_disableAutocorrection(_:)
diffableTextViews_font(_:)
diffableTextViews_foregroundColor(_:)
diffableTextViews_multilineTextAlignment(_:)
diffableTextViews_onSubmit(_:)
diffableTextViews_submitLabel(_:)
diffableTextViews_textContentType(_:)
diffableTextViews_textFieldStyle(_:)
diffableTextViews_textInputAutocapitalization(_:)
diffableTextViews_tint(_:)

Styles

NumberTextStyle (Source, Tests)

A style that binds localized numbers using various formats.

Features

Feature Description
🪙 Values Decimal, Double and (U)Int(8-64)
Optionals Optional and non-optional values
🏹 Precision Up to 38 digits of precision
🧱 Bounds Values are clamped to bounds
🎨 Formats Number, currency and percent
🏞️ Locales Supports every Foundation.Locale
2️⃣ Bilingual Accepts both local and ASCII inputs 

Examples

DiffableTextFieldXAmount.gif

import DiffableTextViews
import SwiftUI

//*============================================================================*
// MARK: View
//*============================================================================*

struct DiffableTextFieldXAmount: View {

    //=------------------------------------------------------------------------=
    // MARK: State
    //=------------------------------------------------------------------------=

    @State var amount = 0 as Decimal
    @State var locale = Locale(identifier: "sv_SE")

    //=------------------------------------------------------------------------=
    // MARK: Body
    //=------------------------------------------------------------------------=

    /// default precision is chosen based on currency
    var body: some View {
        DiffableTextField(value: $amount) {
            .currency(code: "SEK")
            .bounds((0 as Decimal)...)
            // .precision(integer: 1..., fraction: 2)
        }
        .environment(\.locale, locale)
        .diffableTextViews_keyboardType(.decimalPad)
    }
}

PatternTextStyle (Source, Tests)

A style that processes characters laid out in custom patterns.

Features

Feature Description
🏁 Pattern Characters are laid out as described by a pattern
♟️ Placeholders Placeholders represent not-yet-assigned values
Independance Supports multiple placeholders with different rules
👻 Invisibility Pattern suffix can easily be \.hidden()

Examples

DiffableTextFieldXPhone.gif

import DiffableTextViews
import SwiftUI

//*============================================================================*
// MARK: View
//*============================================================================*

struct DiffableTextFieldXPhone: View {

    //=------------------------------------------------------------------------=
    // MARK: State
    //=------------------------------------------------------------------------=

    @State var number: String = ""
    @State var style = PatternTextStyle<String>
        .pattern("+## (###) ###-##-##")
        .placeholder("#") { $0.isASCII && $0.isNumber }
        .equals(())
    
    //=------------------------------------------------------------------------=
    // MARK: Body
    //=------------------------------------------------------------------------=
    
    var body: some View {
        DiffableTextField(value: $number, style: style)
            .diffableTextViews_keyboardType(.numberPad)
    }
}

WrapperTextStyle(s) (Source, Tests)

Decorative styles that modify the behavior of their content.

Style Description Method
Constant Prevents style transformations constant()
Equals Binds the style's equality to a proxy value equals(_:)
Comments
  • Add `measurement` styles.

    Add `measurement` styles.

    I think it would be a neat addition, and it would not be too complicated to add. That said, I thought about it and decided against it. I’m unpaid and unemployed, and I have no need for it myself. Can’t eat kilometers.

    enhancement no 
    opened by oscbyspro 12
  • Miscellaneous improvements

    Miscellaneous improvements

    I’m rather content with the state of the project, but there are a couple of things I’d like to improve, but that are not significant enough to warrant separate issues. So, instead, I’ll collect them here.

    enhancement brrr 
    opened by oscbyspro 9
  • Improved `Encoding` protocol.

    Improved `Encoding` protocol.

    static func index(at position: Position<Self>, in characters: String) -> Index
    static func position(at index: Index, in characters: String) -> Position<Self>
    

    The above implementation makes calculating a Range O(n + m).

    static func index(at distance: Offset<Self>, from start: Index, in snapshot: Snapshot) -> Index
    static func distance(from start: Index, to end: Index, in snapshot: Snapshot)  ->  Offset<Self>
    

    This alternative implementation makes it O(max(n, m)) instead (faster).


    Position was renamed as Offset so that the signature makes more sense.

    brrr 
    opened by oscbyspro 9
  • Support: optional numbers.

    Support: optional numbers.

    When an optional value is nil the view should show a placeholder. While supporting Optional<T> is orthogonal to number processing, and arguably does little to improve UX, it’s like a 0th class citizen in Swift-ville and people just expect it to work, even if SwiftUI.TextField doesn’t support it.

    enhancement 
    opened by oscbyspro 9
  • Support reentrant updates

    Support reentrant updates

    Introduction

    DiffableTextField(value: $optional, style: .number.suffix(optional != nil ? “ items” : “”))
    

    Styles such as the one above (that depend on the input/output value) resolve changes between some and none in two steps, and sometimes two frames. While somewhat difficult to notice, it is also difficult to unsee. It is less than ideal and can easily be fixed.

    Proposal

    When the value changes, wait for it to stabilize before applying any downstream changes.

    Alternatives

    A) Conditional single-pass modifiers, like nonblankSuffix(_:) (more options/complexity).

    enhancement 
    opened by oscbyspro 7
  • Environment overrides `Style/onSetup(_:)`

    Environment overrides `Style/onSetup(_:)`

    In v4.0.0+ it works like it does in SwiftUI. I thought it was rather neat, however, that NumberTextStyle could choose the correct keyboard automatically. So I will have to think about how I can reintroduce this feature, so it works alongside the environment.

    no 
    opened by oscbyspro 7
  • Improve `nil` + `prefix(_:)` & `suffix(_:)` interaction

    Improve `nil` + `prefix(_:)` & `suffix(_:)` interaction

    The prefix(_:) and suffix(_:) modifiers #144 work well for nonoptional numbers, but not so well when an optional number is nil. I haven’t had time to think about the problem, yet, or contemplate what the ideal behavior is. I'm aware, however, and this is my TODO note.

    bug 
    opened by oscbyspro 6
  • Optimize upstream changes while view is idle.

    Optimize upstream changes while view is idle.

    While it’s idle, upstream changes (slider bound to value, etc) are assigned fake attributes because snapshotting would be wasteful. It is still an O(n) operation, however. It is possible to remove this calculation with a pseudo-enum (Bool -> Snapshot/String). It would be simpler if associated enum values were in-place mutable, but they aren’t (which is sad).

    brrr 
    opened by oscbyspro 6
  • Add a `modified total` precision option.

    Add a `modified total` precision option.

    Introduction

    Integers can specify precision in terms of a total number of digits. This is because integer precision is implemented and it happens to be the same thing for integer numbers. It should be possible, however, to implement a dynamic solution that enables the corresponding behavior for floating point numbers as well.

    Why total and not significant digits?

    Because, well, significant digits suck. Image this Float16 number: 0.00[0220]00. Float16 has 3 decimal digits of precision; the number is valid but entries outside the brackets are invalid, or have otherwise unpredictable behavior from the perspective of the user. To make the input experience intuitive, the input space starts at zero and expands contiguously from there to the maximum number of digits.

    Why modified total and not total digits?

    Because ignoring the leading zero in 0.[333] makes sense. The intuitive input rule should really be restated as: expand from the fraction separator up to maximum precision, and allow an integer zero by convention / for aesthetic reasons.

    Proposal(s)

    A) An enum with dynamic behavior. B) A precision protocol with existential box.

    enhancement 
    opened by oscbyspro 5
  • SwiftUI toolbar does not appear above keyboard.

    SwiftUI toolbar does not appear above keyboard.

    SwiftUI.TextField can add a toolbar above the keyboard as shown below.

    view.toolbar {
        ToolbarItemGroup(placement: .keyboard) {
            Spacer()
            Button("Done") { /* action */ }
        }
    }
    

    This does not work on DiffableTextField, however. It should be fixed, if possible, otherwise an alternative should be offered.

    opened by oscbyspro 5
  • UIViewRepresentableContext

    UIViewRepresentableContext

    Learned that UIViewRepresentableContext provides environment values. It can/should replace the use of environment observers. https://developer.apple.com/documentation/swiftui/uiviewrepresentablecontext

    brrr 
    opened by oscbyspro 5
  • Consider `@_spi` attribute

    Consider `@_spi` attribute

    There are a couple of useful features on the underscored attributes list. One of them is @_spi, which can be used to reduce implementation bloat #130 beyond what can be achieved otherwise. An alternative is, of course, to wait for a formal feature introduction.

    WARNING: […] Usage of these attributes […] is STRONGLY DISCOURAGED.

    brrr 
    opened by oscbyspro 1
  • Awaiting improved interoperability.

    Awaiting improved interoperability.

    Public UIKit compatible environment values

    Mimicking environment values adds a lot of overhead, for library authors and especially for users. Nobody wants 12 different modules with 12 different ways of writing the same value to the environment. In an alternative universe, where environment values are exposed, there is peace and harmony and all nations sing Kumbayah because custom 3rd party iOS libraries just work as if Apple themselves had built them.

    Use of SwiftUI.Font, environment \.font

    Requires public SwiftUI.Font to UIKit.UIFont conversion.

    Use of view.toolbar as input accessory view

    Requires exposure of whatever is used behind the scenes.

    Use of other environment values

    Private environment values == decent 3rd party libraries. Public environment values == amazing 3rd party libraries.

    enhancement await 
    opened by oscbyspro 3
Releases(v5.0.0)
  • v5.0.0(Sep 16, 2022)

    Description

    Reworked n’ improved. Such effort. Much wow.

    Notes (see v5.0.0)

    #164 added toolbarDoneButton(_:) #162 the view now dismisses on cyclical updates #161 now supports reentrant updates #156 prevents crash for large integers inputs (!) #152 added .normal style #151 miscellaneous improvements #144 added prefix(_;) and suffix(_:) modifiers #138 added number total digits precision option #121 added standalone() modifier (niche) #108 reworked styles, added an inout Cache #106 changed standard font to plain .body

    Source code(tar.gz)
    Source code(zip)
  • v5.0.0-beta.4(Aug 27, 2022)

  • v5.0.0-beta.3(Aug 26, 2022)

    Changes since v5.0.0-beta.2

    #164 Added a simple diffableTextViews_toolbarDoneButton(_:) modifier

    #162, #155 View now dismisses on cyclical updates (invalid values from outside the view) (!)

    #151 A couple of additional miscellaneous improvements

    Source code(tar.gz)
    Source code(zip)
  • v5.0.0-beta.2(Aug 23, 2022)

  • v5.0.0-beta.1(Aug 17, 2022)

  • v5.0.0-beta(Aug 15, 2022)

  • v4.2.1(Jun 7, 2022)

    Just the latest and greatest before I look into dub dub beta software.

    • some performance improvements #105

    Also, this is commit 1000 + 1337. It must be elite, the number says so.

    Source code(tar.gz)
    Source code(zip)
  • v4.2.0(May 27, 2022)

  • v4.1.0(May 20, 2022)

  • v4.0.1(May 10, 2022)

  • v4.0.0(May 9, 2022)

    Much wow. Such amaze. Very impress.

    Added

    • Optional numbers #86 #92
    • SwiftUI-esque environment values #89
    • Placeholder text #87

    Removed

    • ProxyTextField #89

    Fixed

    • Disabled marked text (not text selection) #88

    Environment

    environment(\.locale, _:)
    environment(\.layoutDirection, _:)
    diffableTextViews_disableAutocorrection(_:)
    diffableTextViews_font(_:)
    diffableTextViews_foregroundColor(_:)
    diffableTextViews_multilineTextAlignment(_:)
    diffableTextViews_onSubmit(_:)
    diffableTextViews_submitLabel(_:)
    diffableTextViews_textContentType(_:)
    diffableTextViews_textFieldStyle(_:)
    diffableTextViews_textInputAutocapitalization(_:)
    diffableTextViews_tint(_:)
    

    Improved names and type aliases

    NumberTextStyle<T> // _NumberTextStyle<T.NumberTextFormat>
    T.NumberTextStyle  // _NumberTextStyle<T.NumberTextFormat>
    
    NumberTextStyle<T>.Percent // _NumberTextStyle<T.NumberTextFormat.Percent>
    T.NumberTextStyle .Percent // _NumberTextStyle<T.NumberTextFormat.Percent>
    
    NumberTextStyle<T>.Currency // _NumberTextStyle<T.NumberTextFormat.Currency>
    T.NumberTextStyle .Currency // _NumberTextStyle<T.NumberTextFormat.Currency>
    
    NumberTextStyle<T?> // _OptionalNumberTextStyle<T.NumberTextFormat>
    T?.NumberTextStyle  // _OptionalNumberTextStyle<T.NumberTextFormat>
    
    NumberTextStyle<T?>.Percent // _OptionalNumberTextStyle<T.NumberTextFormat.Percent>
    T?.NumberTextStyle .Percent // _OptionalNumberTextStyle<T.NumberTextFormat.Percent>
    
    NumberTextStyle<T?>.Currency // _OptionalNumberTextStyle<T.NumberTextFormat.Currency>
    T?.NumberTextStyle .Currency // _OptionalNumberTextStyle<T.NumberTextFormat.Currency>
    
    Source code(tar.gz)
    Source code(zip)
  • v3.6.0(Apr 21, 2022)

    DiffableTextField now behaves more like SwiftUI.TextField.

    Dismisses keyboard on submit

    DiffableTextField now automatically dismisses the keyboard on submit.

    Triggers stack

    The following methods now stack:

     onSetup(of:\_:)
    onUpdate(of:\_:)
    onSubmit(of:\_:).
    

    This behavior is opted out of by using:

     onSetupScope(of:)
    onUpdateScope(of:)
    onSubmitScope(of:),
    

    similar to SwiftUI’s modifier submitScope().

    Source code(tar.gz)
    Source code(zip)
  • v3.5.1(Apr 18, 2022)

  • v3.5.0(Apr 15, 2022)

  • v3.4.2(Apr 4, 2022)

  • v3.4.1(Apr 3, 2022)

  • v3.4.0(Apr 3, 2022)

  • v3.3.2(Apr 2, 2022)

    Reworked the effect of .equals(_:) as described by #78

    DiffableTextField now discards new styles when equal.

    • the behavior without .equals(_:) is unchanged
    • poor use of .equals(_:) can no longer result in an invalid state
    • .equals(_:) can now be used to determine when the view’s style is updated

    Example: .equals(())

    The first style is accepted, while subsequent styles are discarded without comparison.

    Example: .equals(proxy)

    The first style is accepted, while subsequent styles are discarded unless the their proxies are dissimilar.

    Source code(tar.gz)
    Source code(zip)
  • v3.3.1(Apr 1, 2022)

  • v3.3.0(Apr 1, 2022)

    Changes to ConstantTextStyle

    Problem: .constant() applied the effect of .equals(()).

    • .constant() now only prevents changes via .locale(_:)
    • the previous behavior is now written as.constant().equals(())

    Implementation details in preparation of opaque type constraints #74

    • DiffableTextStyleXiOS was reduced into DiffableTextStyle
    • DiffableTextViewsXiOS was split into DiffableTextViewsXiOS and DiffableTextKitXiOS
    Source code(tar.gz)
    Source code(zip)
  • v3.2.3(Mar 29, 2022)

  • v3.2.1(Mar 26, 2022)

  • v3.2.0(Mar 24, 2022)

    It is now possible to set .leading, .center or .trailing text alignment:

    proxy.text.alignment(_:)
    view.multilineTextAlignment(_:) // read once on setup, similar to SwiftUI.TextField
    
    Source code(tar.gz)
    Source code(zip)
  • v3.1.4(Mar 22, 2022)

  • v3.1.3(Mar 20, 2022)

  • v3.1.2(Mar 18, 2022)

    Fixed and improved: virtualization of currency labels.

    The method for marking currency labels as virtual was fixed and improved. All cases when it previously failed used disjoint character sets, so it was inconsequential. However, the new method is more performant for locale-currency pairs where it is needed and skipped for locale-currency pairs where it is not (most). Currency text style performance goes brrr.

    Source code(tar.gz)
    Source code(zip)
  • v3.1.1(Mar 16, 2022)

  • v3.1.0(Mar 15, 2022)

    Currency text style's default behavior has been changed

    Default fraction limits (precision) now depends on currency (matches Foundation.NumberFormatter).

    NumericTextStyle<Decimal>.Currency(code: "USD") ==
    NumericTextStyle<Decimal>.Currency(code: "USD").precision(integer: 1..., fraction: 2...2)
    
    Source code(tar.gz)
    Source code(zip)
  • v3.0.3(Mar 12, 2022)

  • v3.0.2(Mar 11, 2022)

    ProxyTextField.Selection/marked no longer crashes when UITextField.markedTextRange returns nil. Some trivia:

    • UITextField.text is never nil.
    • UITextField.selectedTextRange is never nil.
    • UITextField.markedTextRange is sometimes nil.
    Source code(tar.gz)
    Source code(zip)
Owner
Oscar Byström Ericsson
I tried to be normal once.
Oscar Byström Ericsson
A curated list of Open Source example iOS apps developed in Swift

 A curated list of Open Source example iOS apps developed in Swift. An amazing list for people who are beginners and learning ios development and for ios developers who need any example app or feature.

Jogendra 752 Dec 31, 2022
CodeEdit App for macOS – Elevate your code editing experience. Open source, free forever.

CodeEdit for macOS CodeEdit is a code editor built by the community, for the community, written entirely and unapologetically for macOS. Features incl

CodeEdit 15.8k Dec 31, 2022
SwiftUI package to present a Bottom Sheet interactable view with the desired Detents. Also known as Half sheet.

BottomSheetSUI BottomSheetSUI is a package that gives you the ability to show a Bottom sheet intractable, where you can add your own SwiftUI view. You

Aitor Pagán 8 Nov 28, 2022
A utility application to capture and review search results from Swift Package Index.

SPISearch An app (macOS & iOS) to explore the search results from Swift Package Index. Testflight Links: SPIIndex (iOS and macOS apps) Search Ranking

Joseph Heck 5 Jul 26, 2022
The source of V2er.iOS

V2er-iOS A beautiful V2EX client built for iOS platform. This project is under develop, is not avaiable in App store yet, you could download it from h

v2er.app 203 Jan 5, 2023
NStack is a SwiftUI view that allows you to hoist navigation state into a Coordinator

An NStack allows you to manage SwiftUI navigation state with a single stack property. This makes it easy to hoist that state into a high-level view, such as a coordinator. The coordinator pattern allows you to write isolated views that have zero knowledge of their context within the navigation flow of an app.

John Patrick Morgan 469 Dec 27, 2022
Compose is a library that helps you compose complex and dynamic views.

Compose is a data driven library that will help compose your complex and dynamic Views. It helps you create complex and dynamic Views using easy and s

OLX Brasil 123 Jun 9, 2021
This library allows you to make any UIView tap-able like a UIButton.

UIView-TapListnerCallback Example To run the example project, clone the repo, and run pod install from the Example directory first. Installation UIVie

wajeehulhassan 8 May 13, 2022
iOS App that helps you breath properly.

Breathing App iOS App that helps you breath properly. I created this iOS app in my Intro to iOS Develepmont class at Hunter College. I am not a profes

Kevin Salamanca 0 Dec 24, 2021
iOS constraint maker you always wanted. Write constraints like sentences in English. Simple

YeahLayout iOS constraint maker you always wanted. Write constraints like sentences in English. Simple. Intuitive. No frightening abstractions. One fi

Андрей Соловьев 1 Jan 10, 2022
An iOS application enables you explore art works provided by DeviartArt.com with high quality UX.

Iris.iOS Iris is a model mobile application based on iOS. It provides basic functions allow users to explore on DeviantArt and check Daily Arts, Notif

Xueliang Chen 63 Dec 13, 2022
Using the UIKitChain framework, You can create a UIKit component in one line of code.

Using the UIKitChain framework, You can create a UIKit component in one line of code. Installation CocoaPods CocoaPods is a dependency manager for Coc

Malith Nadeeshan 1 Sep 1, 2022
Flixtor-iOS - iOS streaming app inspired by Netflix that allows you to watch any film and series

Flixtor-iOS iOS streaming app inspired by Netflix that allows you to watch any f

Kevin Liu 0 Jan 14, 2022
Build 1 scene, let AutoLayoutMagic generate the constraints for you!

Auto Layout Magic Create 1 scene, let Auto Layout Magic generate the constraints for you Hello friends, We've all been there. You have an app supporti

Matt 61 Sep 21, 2019
Enables you to hide ur UIViews and make them screen/screen shot proof. objective c/c++ only

SecureView Enables you to hide ur UIViews and make them screen/screen shot proof. objective c/c++ only Usage UIWindow* mainWindow; - (void) setup {

Red16 6 Oct 13, 2022
UIKit Practice Project – Simple app to store names along with photos of people you've met

People UIKit Practice Project #10 – Simple app to store names along with photos of people you've met Cool Features Light & dark mode support Responsiv

foxster.mp4 2 Nov 28, 2022
WHAT WILL YOU LEARN? Onboarding Screen with Page Tab View, state of the app with the new App Storage

WHAT WILL YOU LEARN? Onboarding Screen with Page Tab View, state of the app with the new App Storage Onboarding or a Home screen Understand how the new App Life Cycle works Link View 
 Group Box View Disclosure View Dynamically List View with a loop

Ghullam Abbas 5 Oct 17, 2022
Library that makes it easy to create multiple environments within a single app. You can switch environments without deleting the application.

AppContainer Library that makes it easy to create multiple environments within a single app. You can switch environments without deleting the applicat

null 8 Dec 15, 2022
A tiny category on UIView that allows you to set one property: "parallaxIntensity" to achieve a parallax effect with UIMotionEffect

NGAParallaxMotion A tiny category on UIView that allows you to set one property: parallaxIntensity to achieve a parallax effect with UIMotionEffect. S

Michael Bishop 650 Sep 26, 2022