Giter VIP home page Giter VIP logo

apple-inspired-puller's Introduction

πŸ₯‡ Apple-Inspired Puller

Apple has released a sheet presentation controller in iOS 15, which works great. In iOS 16, developers can customize the detents of the sheet. However, we would like to use these features in iOS 14 as well. That's why I have developed a puller presentation controller with the same API as Apple's. The puller works even in iOS 11. Please feel free to use the puller and give it a star if you find it useful.

🌈 Features

  • Extremely easy to use with a native API-like interface.
  • Customizable puller presentation animation with two presets: default and spring.
  • Compatible with iOS 11+, the puller offers four preset detents: medium, large, full and fitsContent. In the full state, the puller supports device corners, like the Apple Music app. When using the fitsContent state, the puller adapts its height to the default height of the presenting view controller's view. Additionally, you can set as many custom detents you want.
  • Supports inside and outside drag indicators.
  • Supports interactive pop gesture.
  • Supports SwiftUI.
  • Works seamlessly with ScrollView.
  • Compatible with keyboard and device rotation.
  • Can be opened in a similar style to the AirPods Pro sheet as shown by Apple.
  • Includes various settings for customization and control.
  • Unfortunately, the puller doesn’t support view controllers like UIColorPickerViewController cause they are running in another process and using UIRemoteView under the hood.

πŸ’‘ Attention

The puller uses a specific approach to obtain the screen corner radius by adding a displayCornerRadius property to UIScreen, which reads the private _displayCornerRadius. The selector somewhat obscured, which usually means it will get past app review. However, use at your own risk!

πŸ—οΈ Installation

Apple-Inspired Puller is available through SPM. Just add this repository as a dependency to your package or project.

dependencies: [
    .package(url: "https://github.com/zsergey/Apple-Inspired-Puller.git", branch: "develop")
]

πŸ›  Examples

Default puller

Default puller with three detents: custom, medium and large. Drag indicator is inside.

Source Code
let viewController = UIViewController()

let pullerModel = PullerModel(detents: [.custom(0.25), .medium, .full], 
                              dragIndicator: .inside(.black))

presentAsPuller(viewController, model: pullerModel)

Spring puller

Spring puller in full state. Drag indicator is outside.

Source Code
let viewController = UIViewController()

let pullerModel = PullerModel(animator: .spring, 
                              detents: [.full], 
                              dragIndicator: .outside(.black))

presentAsPuller(viewController, model: pullerModel)

Supporting interactive pop gesture

Spring puller in full state that supports interactive pop gesture.

Source Code
let viewController = UIViewController()

let pullerModel = PullerModel(animator: .spring, 
                              detents: [.full], 
                              supportsInteractivePopGesture: true) // it's true by default

presentAsPuller(viewController, model: pullerModel)

Dialog style

Dialog style puller resembles Apple's AirPods Pro sheet.

Source Code
let viewController = UIViewController()

let pullerModel = PullerModel(animator: .spring, 
                              detents: [.medium], 
                              hasDynamicHeight: false)

presentAsPuller(viewController, model: pullerModel)

How to use fitsContent detent

The intrinsicContentSize property of the view controller being presented must be set for the fitsContent detent to work properly in the puller. If the intrinsicContentSize property is not set, the puller will default to the large detent.

Source Code
class YourViewController: UIViewController {

    override func loadView() {
        view = ResizableView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        (view as? ResizableView)?.defaultHeight = 300
    }
}

class ResizableView: UIView {
    
    var defaultHeight: CGFloat? 
    
    override var intrinsicContentSize: CGSize {
        if let defaultHeight = defaultHeight {
            return CGSize(width: UIView.noIntrinsicMetric, height: defaultHeight)
        }
        return super.intrinsicContentSize
    }
}

let viewController = YourViewController()

let pullerModel = PullerModel(animator: .spring, 
                              detents: [.fitsContent])

presentAsPuller(viewController, model: pullerModel)

Supports SwiftUI

You can present PullerHostingController with any SwiftUI view.

Source Code
let viewController = PullerHostingController(rootView: DemoScrollView())

let pullerModel = PullerModel(animator: .spring, 
                              detents: [.custom(0.25), .medium, .full], 
                              dragIndicator: .outside(.black))

presentAsPuller(viewController, model: pullerModel)

Also you can apply the .puller modifier to any SwiftUI view, ensuring you attach a binding to the isPresented property β€” just like the standard .sheet modifier.

Source Code
struct DemoPullerContent: View {
    @Environment(\.dismiss) var dismiss

    var body: some View {
        VStack {
            Spacer()
            Text("Eat some more of these soft French buns and drink some tea.")
            Spacer()
            Button("Close") {
                dismiss()
            }
            .padding(.bottom, 20)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

struct ContentView: View {
    @State private var isPresented = false

    var body: some View {
        Button("Present") {
            isPresented.toggle()
        }
        .puller(isPresented: $isPresented, model: PullerModel(detents: [.medium]), content: DemoPullerContent.init)
    }
}

An image in the puller

You can show the puller with an image.

Source Code
let viewController = ImageViewController()

let pullerModel = PullerModel(animator: .spring, 
                              detents: [.fitsContent], 
                              dragIndicator: .outside(.black))

presentAsPuller(viewController, model: pullerModel)

Settings in Demo

Demo app includes settings for adjusting animation speed and customizing puller behavior.

πŸ₯ Author

You can find me on Twitter @zsergey

πŸŽ‰ Contributing

Feel free to add issues or pull requests here on GitHub. I cannot guarantee that I will accept your changes, but feel free to fork the repo and make changes as you see fit. Thanks!

πŸŽ“ License

Apple-Inspired Puller is released under the MIT license. See LICENSE for more information.

apple-inspired-puller's People

Contributors

zsergey avatar rusik avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.