All Projects → 0xNSHuman → StackFlowView

0xNSHuman / StackFlowView

Licence: MIT license
Enforce stack behaviour for custom UI flow.

Programming Languages

swift
15916 projects
ruby
36898 projects - #4 most used programming language

Projects that are alternatives of or similar to StackFlowView

Coinverse
Coinverse Open App is the first audiocast app for cryptocurrency news. 🚀
Stars: ✭ 133 (+280%)
Mutual labels:  feed, paging
Xlbubbletransition
iOS ViewController间切换的转场动画
Stars: ✭ 127 (+262.86%)
Mutual labels:  pop, push
VBA-Arrays
😎 Array functions that are similar JavaScript functions. Example: Push, Pop, Shift, Unshift, Sort, length, toString.
Stars: ✭ 48 (+37.14%)
Mutual labels:  pop, push
staticstep
Provides truly zero-cost alternatives to Iterator::step_by for both incrementing and decrementing any type that satisfies RangeBounds<T: Copy + Default + Step>.
Stars: ✭ 13 (-62.86%)
Mutual labels:  steps, step
PushAll
PushAll让推送集成更简洁、快速
Stars: ✭ 28 (-20%)
Mutual labels:  push
xml-to-json
Simple API that converts dynamic XML feeds to JSON through a URL or pasting the raw XML data. Made 100% in PHP.
Stars: ✭ 38 (+8.57%)
Mutual labels:  feed
TIFFStack
Load TIFF files into matlab fast, with lazy loading
Stars: ✭ 32 (-8.57%)
Mutual labels:  stack
Competitive Programming
Contains solutions and codes to various online competitive programming challenges and some good problems. The links to the problem sets are specified at the beginning of each code.
Stars: ✭ 65 (+85.71%)
Mutual labels:  stack
Competitive Coding
Contains Solution for all type of Problems of Competitive Programming. Updates Frequently as any problem is solved.
Stars: ✭ 16 (-54.29%)
Mutual labels:  stack
odin
High level 2d game engine written in Haskell.
Stars: ✭ 28 (-20%)
Mutual labels:  stack
DuckOS
Such OS; Very Duck!
Stars: ✭ 16 (-54.29%)
Mutual labels:  paging
takomo
Organize, parameterize and deploy your CloudFormation stacks
Stars: ✭ 27 (-22.86%)
Mutual labels:  stack
stack
🥭 nxpm-stack lets you generate a complete and opinionated full-stack application in a Nx Workspace, ready to extend and deploy!
Stars: ✭ 98 (+180%)
Mutual labels:  stack
Algorithms
Data Structures & Algorithms. Includes solutions for Cracking the Coding Interview 6th Edition
Stars: ✭ 89 (+154.29%)
Mutual labels:  stack
Cyca
Web-based bookmarks and feeds manager
Stars: ✭ 15 (-57.14%)
Mutual labels:  feed
datagov-deploy
Main repository for the data.gov service
Stars: ✭ 156 (+345.71%)
Mutual labels:  stack
GemBox.Email.Examples
Read and write email files (MSG, EML, MHTML), and compose, receive and send email messages using POP, IMAP, SMTP, and EWS in a simple and efficient way.
Stars: ✭ 18 (-48.57%)
Mutual labels:  pop
andpush
Android Push Notification in Ruby: The fastest client for FCM (Firebase Cloud Messaging)
Stars: ✭ 83 (+137.14%)
Mutual labels:  push
DEPRECATED-data-structures
A collection of powerful data structures
Stars: ✭ 2,648 (+7465.71%)
Mutual labels:  stack
flutter examples
Random flutter examples
Stars: ✭ 18 (-48.57%)
Mutual labels:  stack

StackFlowView

Licence Version Swift Version StackFlowView

📥 Build custom UI flow using stack order 📤

🔗 Enforce sequential user interaction 🔗

🗂 Focus user attention on one flow step at a time 🗂

Multiple stacks at once Cards stack Stack of pages

Stack of stacks


How does it work?

StackFlowView is a high-level view capable of hosting a collection of custom UIViews. Which is, well, not unique behaviour.. The special thing about it though, is that it enforces stack flow behaviour (as the name suggests), which means:

  1. Only the last view in stack allows user interaction. There is no way to affect past or future state of the UI flow;

  2. No view properties can be pre-determined until the moment before putting one into stack (push action). This way, every next stack item considers previous state and can be adjusted to reflect particular flow step;

  3. It is not possible to go N items back without dismissing/destroying those items (pop action). This way, going back in time and changing state enforces subsequent flow steps to be revisited.

During development, various state-dependent UX cases were kept in mind. For example, this solution perfectly works for all kinds of dynamic input forms where every next set of options depends on previous choices made by user.

Installation

CocoaPods

  1. Add pod 'StackFlowView' to your Podfile;
  2. Run pod install or pod update in Terminal;
  3. Re-open your project using .xcworkspace, put import StackFlowView in the swift files you plan to use stack flow in (or use bridging in Obj-C projects);
  4. Rebuild and enjoy.

Old School Way

Drop folder with .swift source files to your project and you're done.

Usage

Creation

Creating StackFlowView takes a few lines of code. Basically, you need to:

  • Initialize it with any frame (not necessery);
  • Add it to superview;
  • Set delegate;
  • Optionally set up constraints if you want to enjoy autolayout-ready behaviour;
let stackView = StackFlowView() // StackFlowView(frame: ...)
stackView.delegate = self

view.addSubview(stackView)

/* — Optional constraints — */

([
	NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0),
	NSLayoutConstraint(item: stackView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0),
	NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0),
	NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0)
]).forEach { $0.isActive = true }

view.setNeedsLayout()
view.layoutIfNeeded()

Customization

There are some nice options to define desired behaviour of your stack flow, including its direction of growth, the way it separates items, gestures user can use to move back and forth, and more. Please see the comments below, as well as property references in Xcode.

// How big should padding next to the stack's last item be?
stackView.headPadding = 0

// Which direction should new items be pushed in?
stackView.growthDirection = .down

// Separate by lines, padding or nothing at all?
stackView.separationStyle = .line(thikness: 2.0, color: .black)
						 // .padding(size: 20.0)
						 // .none

// If you want your stack gradually fade away, you can pick any of the styles, or combine them!
stackView.fadingStyle = .combined(styles:
	[
		.tint(color: .white, preLastAlpha: 0.9, alphaDecrement: 0.1),
		.gradientMask(effectDistance: stackView.bounds.height * 0.7)
	]
) // Or just .none

// You can swipe up-down or left-right to control your flow, and/or also tap inactive stack area to pop any number of items (depends on where you tap)
stackView.userNavigationOptions = [.swipe, .tap]

// Fast hops or sloooooow animation?
stackView.transitionDuration = 0.25

// Set to false if you don't need automatic safe area detection/adoption
stackView.isSeekingSafeArea = true

// Set to false to turn off stretch-out behaviour for your content items during autolayout updates
stackView.isAutoresizingItems = true

Push and Pop items

NOTE: There is no views reusability mechanism in current version, so whatever you push to StackFlowView increments memory usage until you pop it. Therefore, the weak place of this library is a large number of flow steps. It's in TODO list to address this feature.

Push

There is only one straight-forward to use method to push your view into Stack Flow, but it lets you customize things to the extent you want.

You can stick to using the same style for all items, or use custom approach for each one.

Just push the view itself

Send the view to stack without showing anything else.

Item without header

stackView.push(myCustomView)
Push with header bar (standard appearance)

Display item's title along with standard-looking buttons navigation, which is a good alternative to gestures in case you need it (for example, as an Accessibility option for users).

Item with stanrard header

stackView.push(myCustomView, title: "Step ♦️")
Push with customised header bar

Define custom appearance for the item's container, including its header bar background color, title font and color, and navigation buttons appearance.

Item with custom appearance

// Define custom top bar appearance

let topBarAppearance: StackItemAppearance.TopBar = {
	let popButtonAppearance: StackItemAppearance.TopBar.Button
	let pushButtonAppearance: StackItemAppearance.TopBar.Button

	// You can use images or attributed text for navigation buttons

	let preferIconsOverText = false

	if preferIconsOverText { // Use icons
		popButtonAppearance = StackItemAppearance.TopBar.Button(icon: UIImage(named: "back")!)
		pushButtonAppearance = StackItemAppearance.TopBar.Button(icon: UIImage(named: "forth")!)
	} else { // Use text
		let popButtonTitle = NSAttributedString(string: "♦️⬅️", attributes: [.foregroundColor : UIColor.blue])
		popButtonAppearance = StackItemAppearance.TopBar.Button(title: popButtonTitle)

		let pushButtonTitle = NSAttributedString(string: "➡️💎", attributes: [.foregroundColor : UIColor.blue])
		pushButtonAppearance = StackItemAppearance.TopBar.Button(title: pushButtonTitle)
	}

	let customBarAppearance = StackItemAppearance.TopBar(backgroundColor: Utils.randomPastelColor(), titleFont: .italicSystemFont(ofSize: 17.0), titleTextColor: .white, popButtonIdentity: popButtonAppearance, pushButtonIdentity: pushButtonAppearance)

	return customBarAppearance
}()

// Set appearence for the whole item, including previously created top bar appearance

let customAppearance = StackItemAppearance(backgroundColor: Utils.randomPastelColor(), topBarAppearance: topBarAppearance)

// Push it all to the stack!

stackView.push(myCustomView, title: "Step ♦️", customAppearance: customAppearance)

Pop

Pop N items from stack by calling one of the pop(_:) method variations.

Pop one item
stackView.pop()
Pop multiple items
stackView.pop(numberOfItems)

Delegate methods

StackFlowDelegate protocol enables control over stack flow by the object implementing it. For example, it delivers push and pop intention events triggered by user gestures, and lets you decide if StackFlowView should proceed or ignore this action. It also reports about the corresponding actions that are upcoming or just passed.

func stackFlowViewDidRequestPop(_ stackView: StackFlowView, numberOfItems: Int) {
	log(message: "Requested to go \(numberOfItems) steps back", from: self)
	stackView.pop(numberOfItems)
}

func stackFlowViewDidRequestPush(_ stackView: StackFlowView) {
	log(message: "Requested next item", from: self)
	goToNextStep()
}

func stackFlowViewWillPop(_ stackView: StackFlowView) {
	log(message: "About to go one item back", from: self)
}

func stackFlowViewDidPop(_ stackView: StackFlowView) {
	log(message: "Went one item back", from: self)
}

func stackFlowView(_ stackView: StackFlowView, willPush view: UIView) {
	log(message: "About to to go to the next step", from: self)
}

func stackFlowView(_ stackView: StackFlowView, didPush view: UIView) {
	log(message: "Went to next step with view: \(view)", from: self)
}

[Optional] Simplest flow logic example

class MyFlowController: UIViewController {
	// MARK: - Flow definition -

	enum MyFlowStep: Int {
		case none = -1
		case one = 0, two, three, four

		static var count: Int { return 4 }

		var title: String {
			switch self {
			default:
				return "Step \(shortSymbol)"
			}
		}

		var shortSymbol: String {
			switch self {
			case .one:
				return "♦️"

			case .two:
				return "♠️"

			case .three:
				return "💎"

			case .four:
				return "🔮"

			case .none:
				return ""
			}
		}

		var prevStep: FlowStep? {
			let prevValue = rawValue - 1
			return prevValue >= 0 ? FlowStep(rawValue: prevValue) : nil
		}

		var nextStep: FlowStep? {
			let nextValue = rawValue + 1
			return nextValue < FlowStep.count ? FlowStep(rawValue: nextValue) : nil
		}
	}

    // MARK: - Properties -

    private let stackView = StackFlowView()

    /* — Whenever this property is set, you can prepare the next view to push — */

    private var currentStep: MyFlowStep = .none {
		didSet {
			// Get identity of the current step

			let itemTitle = currentStep.title

			// You can optionall use bounding steps' identity for something like setting custom navigation buttons

			let prevItemSymbol = currentStep.prevStep?.shortSymbol
			let nextItemSymbol = currentStep.nextStep?.shortSymbol

			// Here you should construct your custom UIView considering purposes of this particular step

			let itemView = stepView(for: currentStep)

			// Now you can push your custom view using superclass `push()` method!

			stackView.push(itemView, title: itemTitle)
		}
	}

	// MARK: - View constructor -

	private func stepView(for step: MyFlowStep) -> UIView {
		let stepView: UIView

		// Note this `safeSize` property of StackFlowView. You should use it to get info about its available content area, not blocked by any views outside of safe area

		let safeStackFlowViewWidth = stackView.safeSize.width

		// Build custom view for any given step

		switch step {
		case .one:
			stepView = UIView(frame: CGRect(x: 0, y: 0, width: safeStackFlowViewWidth, height: 100.0))

		case .two:
			stepView = UIView(frame: CGRect(x: 0, y: 0, width: safeStackFlowViewWidth, height: 200.0))

		default:
			stepView = UIView(frame: CGRect(x: 0, y: 0, width: safeStackFlowViewWidth, height: 300.0))
		}

		return stepView
	}
}

TODO

  • Think about views reusability mechanism

License

StackFlowView is released under an MIT license. See the LICENSE file.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].