/Aiolos

A floating panel for your iOS Apps

Primary LanguageSwiftMIT LicenseMIT

Aiolos

Yet another iOS Floating Panel

Carthage Compatible SPM compatible Platform iOS Language Swift Twitter: @myell0w

Aiolos, ancient greek for quick-moving/nimble, is a Swift UI framework inspired by the floating panel, that was introduced to Maps app in iOS 11. Give it a try in MindNode 5 for iOS (free trial available).

It is fully gesture-driven, takes safe area insets into account, has support for right-to-left languages baked in and automatically reacts to the on-screen keyboard. Compared to many other open source panel solutions, Aiolos is designed to be an always-visible child view controller, and therefore does not use the custom view controller transition API of iOS.

MindNode for iPad and iPhone

Integration with Carthage

Add this line to your Cartfile.

github "IdeasOnCanvas/Aiolos"

Integration with Swift Package Manager

Aiolos can be integrated with Swift Package Manager directly within Xcode.

Usage in Code

There's a demo app, that demonstrates how the Panel can be set up with a different configuration for iPhones and iPads.

func makePanel(with contentViewController: UIViewController) -> Panel {
    // create Panel with default configuration
    let configuration = Panel.Configuration.default
    let panelController = Panel(configuration: configuration)

    // specify, which ViewController is displayed in the panel
    panelController.contentViewController = contentViewController

    // setup delegates that handle size configuration and animation callbacks
    panelController.sizeDelegate = self
    panelController.animationDelegate = self

    // change the configuration to fit you needs
    panelController.configuration.position = self.panelPosition(for: self.traitCollection)
    panelController.configuration.margins = self.panelMargins(for: self.traitCollection)
    panelController.configuration.appearance.separatorColor = .white

    // we want a different look/behaviour on iPhone compared to iPad
    if self.traitCollection.userInterfaceIdiom == .pad {
        panelController.configuration.appearance.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
    } else {
        panelController.configuration.supportedModes = [.minimal, .compact, .expanded, .fullHeight]
        panelController.configuration.appearance.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
    }

    return panelController
}

Configuring the size

extension ViewController: PanelSizeDelegate {

    func panel(_ panel: Panel, sizeForMode mode: Panel.Configuration.Mode) -> CGSize {
        let width = self.panelWidth(for: self.traitCollection, position: panel.configuration.position)
        switch mode {
        case .minimal:
            return CGSize(width: width, height: 0.0)
        case .compact:
            return CGSize(width: width, height: 64.0)
        case .expanded:
            let height: CGFloat = self.traitCollection.userInterfaceIdiom == .phone ? 270.0 : 320.0
            return CGSize(width: width, height: height)
        case .fullHeight:
            return CGSize(width: width, height: 0.0)
        }
    }
}

Reacting to Panel animations

extension ViewController: PanelAnimationDelegate {

    func panel(_ panel: Panel, willTransitionTo size: CGSize) {
        print("Panel will transition to size \(size)")
    }

    func panel(_ panel: Panel, willTransitionFrom oldMode: Panel.Configuration.Mode?, to newMode: Panel.Configuration.Mode, with coordinator: PanelTransitionCoordinator) {
        print("Panel will transition from \(oldMode) to \(newMode)")
        // we can animate things along the way
        coordinator.animateAlongsideTransition({
            print("Animating alongside of panel transition")
        }, completion: { animationPosition in
            print("Completed panel transition to \(newMode)")
        })
    }
}

Credits

Aiolos is brought to you by IdeasOnCanvas GmbH, the creator of MindNode for iOS, macOS & watchOS