motion-wizard 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +74 -0
- data/Rakefile +1 -0
- data/lib/motion-wizard.rb +9 -0
- data/lib/motion-wizard/animation_strategy/base_animation_strategy.rb +25 -0
- data/lib/motion-wizard/animation_strategy/ios7_slide_left_to_right.rb +18 -0
- data/lib/motion-wizard/animation_strategy/ios7_slide_right_to_left.rb +18 -0
- data/lib/motion-wizard/animation_strategy/left_to_right.rb +10 -0
- data/lib/motion-wizard/animation_strategy/none.rb +11 -0
- data/lib/motion-wizard/animation_strategy/right_to_left.rb +10 -0
- data/lib/motion-wizard/animation_strategy/slide_animation.rb +21 -0
- data/lib/motion-wizard/controllers/content_controller.rb +17 -0
- data/lib/motion-wizard/controllers/wizard_view_controller.rb +143 -0
- data/lib/motion-wizard/lib/forwardable.rb +12 -0
- data/lib/motion-wizard/views/index_item_view.rb +53 -0
- data/lib/motion-wizard/views/wizard_navigation_bar.rb +55 -0
- data/motion-wizard.gemspec +22 -0
- data/samples/wizard-1/.gitignore +16 -0
- data/samples/wizard-1/Gemfile +8 -0
- data/samples/wizard-1/Rakefile +18 -0
- data/samples/wizard-1/app/app_delegate.rb +8 -0
- data/samples/wizard-1/app/controllers/steps_view_controller.rb +92 -0
- data/samples/wizard-1/app/controllers/wizard_1_view_controller.rb +28 -0
- data/samples/wizard-1/app/views/styles/steps.rb +128 -0
- data/samples/wizard-1/app/views/styles/wizard_1_view_controller.rb +42 -0
- data/samples/wizard-1/resources/Default-568h@2x.png +0 -0
- data/samples/wizard-1/spec/main_spec.rb +9 -0
- data/samples/wizard-2/.gitignore +16 -0
- data/samples/wizard-2/Gemfile +8 -0
- data/samples/wizard-2/Rakefile +22 -0
- data/samples/wizard-2/app/app_delegate.rb +10 -0
- data/samples/wizard-2/app/controllers/steps_view_controller.rb +59 -0
- data/samples/wizard-2/app/controllers/wizard_2_view_controller.rb +29 -0
- data/samples/wizard-2/app/views/my_custom_index_item_view.rb +25 -0
- data/samples/wizard-2/app/views/styles/handlers.rb +7 -0
- data/samples/wizard-2/app/views/styles/index_item.rb +36 -0
- data/samples/wizard-2/app/views/styles/steps.rb +67 -0
- data/samples/wizard-2/app/views/styles/wizard_view_controller.rb +4 -0
- data/samples/wizard-2/resources/Default-568h@2x.png +0 -0
- data/samples/wizard-2/resources/background-568h@2x.png +0 -0
- data/samples/wizard-2/resources/background.png +0 -0
- data/samples/wizard-2/resources/background@2x.png +0 -0
- data/samples/wizard-2/resources/button.png +0 -0
- data/samples/wizard-2/spec/main_spec.rb +9 -0
- data/wizard-1.gif +0 -0
- data/wizard-2.gif +0 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0b0eb05108f916b5e4a2379147a5517ab4f181dd
|
4
|
+
data.tar.gz: afcfe23fb17888d3afd67cb0170b0cf45cc6386b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ebdb9eb94f462dcddf59ad992f40dff1adc1f1b6384e313ecb03f15cbfd6e2ee61fbff466d1cc9c1a25949e15561d2a93db945525470df763b02c2f23b6b9066
|
7
|
+
data.tar.gz: e19250b87f2322a6f8f16c6a5f8e543f739a9d349359b733fffd4cc75300d8e388ee234f6b7a5051fde87c7ef85b3e8e9e058b35db8e08b0a02b524fc50b22de
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ignacio Piantanida
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Motion-Wizard
|
2
|
+
|
3
|
+
A small gem to create clean wizard-like views
|
4
|
+
|
5
|
+
![Sample 1](https://raw.github.com/ijpiantanida/motion-wizard/master/wizard-1.gif)
|
6
|
+
![Sample 2](https://raw.github.com/ijpiantanida/motion-wizard/master/wizard-2.gif)
|
7
|
+
|
8
|
+
#Installation
|
9
|
+
#How to use it
|
10
|
+
Your custom wizard view controller will inherit from `MotionWizard::WizardViewController`.
|
11
|
+
|
12
|
+
With the `steps` class method, you specify which view controllers will describe your wizard.
|
13
|
+
|
14
|
+
``` ruby
|
15
|
+
class Wizard1ViewController < MotionWizard::WizardViewController
|
16
|
+
steps Step1ViewController,
|
17
|
+
Step2ViewController,
|
18
|
+
Step3ViewController,
|
19
|
+
Step4ViewController
|
20
|
+
end
|
21
|
+
```
|
22
|
+
##Moving through steps
|
23
|
+
The `WizardViewController` will create instances of your steps controllers which will then be extended with the `MotionWizard::ContentController` module. This module gives you the following methods which can be used inside your steps:
|
24
|
+
* **#next(data)**: Will move to the next step. It will call `#finish` if there is no following step.
|
25
|
+
* **#previous(data)**: Will move to the previous step. If already on the first step, this method will do nothing.
|
26
|
+
* **#go_to_step(step_number, data)**: Will go to the selected step.
|
27
|
+
* **#finish(data)**: Will call the `#when_finished` method on the wizard view controller
|
28
|
+
|
29
|
+
When moving through steps, you can pass an optional hash parameter with custom data that will get merged into a common wizard hash. You can access this hash inside your steps with the `#wizard_data` method.
|
30
|
+
|
31
|
+
``` ruby
|
32
|
+
class Step1ViewController
|
33
|
+
...
|
34
|
+
|
35
|
+
def viewWillAppear(animated)
|
36
|
+
super
|
37
|
+
@button.on(:touch){ self.next(step_1_text: "Awesome!") }
|
38
|
+
end
|
39
|
+
|
40
|
+
...
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
Your wizard view controller class can define a `#when_finished` method that will get called when any of the steps view controller call `#finish` or `#next` when already on the last step. Within this method you have also access to `wizard_data`.
|
45
|
+
|
46
|
+
``` ruby
|
47
|
+
class Wizard1ViewController < MotionWizard::WizardViewController
|
48
|
+
def when_finished
|
49
|
+
NSLog "You can access your data here #{wizard_data}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
##Customizing Index Views
|
55
|
+
By default, index views will be instances of `MotionWizard::IndexItem`. This view has a `label_wrapper` (`UIView`) subview, which has a `label` (`UILabel`) subview.
|
56
|
+
|
57
|
+
It also defines two methods `when_selected` and `when_unselected` that allow to subscribe a block to be called whenever the current step changes.
|
58
|
+
|
59
|
+
You can customize the default index view appearance and behaviour by defining the `set_up_index_item_at(index_item, index)` method inside your `WizardViewController` subclass ([see sample Nr 1](samples/wizard-1)).
|
60
|
+
|
61
|
+
``` ruby
|
62
|
+
class Wizard1ViewController < MotionWizard::WizardViewController
|
63
|
+
def setup_index_item_at(index_item, index)
|
64
|
+
layout(index_item, :index_item)
|
65
|
+
index_item.label.text = "ABCD"[index]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
If you would rather use a custom index item class instead of the default one, you can register it using the `index_item_view_class` class method inside your `WizardViewController` subclass definition.
|
71
|
+
|
72
|
+
Your custom class can define the methods `#select` and `#unselect` to be called whenever the wizard changes the current step.
|
73
|
+
|
74
|
+
([see sample Nr 2](samples/wizard-2))
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,9 @@
|
|
1
|
+
unless defined?(Motion::Project::Config)
|
2
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
3
|
+
end
|
4
|
+
|
5
|
+
Motion::Project::App.setup do |app|
|
6
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'motion-wizard/**/*.rb')).reverse.each do |file|
|
7
|
+
app.files.unshift file
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module MotionWizard
|
2
|
+
module AnimationStrategy
|
3
|
+
class BaseAnimationStrategy
|
4
|
+
attr_accessor :already_on_final_position, :duration
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@duration = 0.5
|
8
|
+
end
|
9
|
+
|
10
|
+
def show_view(view, &after_block)
|
11
|
+
@show_view = view
|
12
|
+
@show_view_after_block = after_block
|
13
|
+
end
|
14
|
+
|
15
|
+
def hide_view(view, &after_block)
|
16
|
+
@hide_view = view
|
17
|
+
@hide_view_after_block = after_block
|
18
|
+
end
|
19
|
+
|
20
|
+
def animate
|
21
|
+
raise "Subclass responsability"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MotionWizard
|
2
|
+
module AnimationStrategy
|
3
|
+
class Ios7SlideLeftToRight < SlideAnimation
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
@duration = 0.25
|
7
|
+
end
|
8
|
+
|
9
|
+
def animate
|
10
|
+
show_view_index = @show_view.superview.subviews.index(@show_view)
|
11
|
+
hide_view_index = @show_view.superview.subviews.index(@hide_view)
|
12
|
+
@show_view.superview.exchangeSubviewAtIndex(hide_view_index, withSubviewAtIndex: show_view_index) if show_view_index > hide_view_index
|
13
|
+
do_slide_animation(@show_view, -70, 0, @show_view_after_block)
|
14
|
+
do_slide_animation(@hide_view, 0, App.frame.size.width, @hide_view_after_block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MotionWizard
|
2
|
+
module AnimationStrategy
|
3
|
+
class Ios7SlideRightToLeft < SlideAnimation
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
@duration = 0.25
|
7
|
+
end
|
8
|
+
|
9
|
+
def animate
|
10
|
+
show_view_index = @show_view.superview.subviews.index(@show_view)
|
11
|
+
hide_view_index = @show_view.superview.subviews.index(@hide_view)
|
12
|
+
@show_view.superview.exchangeSubviewAtIndex(hide_view_index, withSubviewAtIndex: show_view_index) if show_view_index < hide_view_index
|
13
|
+
do_slide_animation(@show_view, App.frame.size.width, 0, @show_view_after_block)
|
14
|
+
do_slide_animation(@hide_view, 0, -70, @hide_view_after_block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MotionWizard
|
2
|
+
module AnimationStrategy
|
3
|
+
class SlideAnimation < BaseAnimationStrategy
|
4
|
+
attr_accessor :direction
|
5
|
+
|
6
|
+
def animate
|
7
|
+
do_slide_animation(@show_view, @direction*App.frame.size.width, 0, @show_view_after_block)
|
8
|
+
do_slide_animation(@hide_view, 0, -@direction*App.frame.size.width, @hide_view_after_block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def do_slide_animation(view, initial_position, final_position, after_block)
|
12
|
+
view.origin = [initial_position, view.origin.y]
|
13
|
+
UIView.animateWithDuration(@duration, animations: -> {
|
14
|
+
view.origin = [final_position, view.origin.y]
|
15
|
+
},completion: ->(finished) {
|
16
|
+
after_block.call(view) if after_block
|
17
|
+
})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MotionWizard
|
2
|
+
module ContentController
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_reader :wizard_view_controller
|
6
|
+
delegate :@wizard_view_controller, :next, :previous, :go_to_step, :wizard_data, :reset!, :finish
|
7
|
+
|
8
|
+
def viewDidLoad
|
9
|
+
super
|
10
|
+
self.view.clipsToBounds = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def wizard_view_controller=(wizard_view_controller)
|
14
|
+
@wizard_view_controller = WeakRef.new(wizard_view_controller)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module MotionWizard
|
2
|
+
class WizardViewController < UIViewController
|
3
|
+
attr_reader :navigation_bar_view, :content_view, :wizard_data
|
4
|
+
|
5
|
+
DEFAULT_NAVIGATION_BAR_HEIGHT = 44
|
6
|
+
|
7
|
+
def self.steps(*steps)
|
8
|
+
@_wizard_steps = steps
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.forward_animation_strategy(animation_strategy_class)
|
12
|
+
@_forward_animation_strategy_class = animation_strategy_class
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.backward_animation_strategy(animation_strategy_class)
|
16
|
+
@_backward_animation_strategy_class = animation_strategy_class
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.index_item_view_class(index_item_view_class)
|
20
|
+
@_index_item_view_class = index_item_view_class
|
21
|
+
end
|
22
|
+
|
23
|
+
def init
|
24
|
+
super
|
25
|
+
@steps_controllers_classes = self.class.instance_variable_get(:@_wizard_steps) || []
|
26
|
+
@forward_animation_strategy_class = self.class.instance_variable_get(:@_forward_animation_strategy_class) || AnimationStrategy::RightToLeft
|
27
|
+
@backward_animation_strategy_class = self.class.instance_variable_get(:@_backward_animation_strategy_class) || AnimationStrategy::LeftToRight
|
28
|
+
@index_item_view_class = self.class.instance_variable_get(:@_index_item_view_class) || IndexItem
|
29
|
+
@current_step = 0
|
30
|
+
@wizard_data = {}
|
31
|
+
@steps_controllers = []
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def viewDidLoad
|
36
|
+
super
|
37
|
+
|
38
|
+
@content_view = UIView.alloc.init
|
39
|
+
@content_view.origin = [0,0]
|
40
|
+
@content_view.size = self.view.size
|
41
|
+
self.view.addSubview(@content_view)
|
42
|
+
|
43
|
+
@navigation_bar_view = WizardNavigationBar.alloc.init_with_number_of_steps(number_of_steps, self)
|
44
|
+
@navigation_bar_view.origin = [0,0]
|
45
|
+
@navigation_bar_view.size = [self.view.size.width, DEFAULT_NAVIGATION_BAR_HEIGHT]
|
46
|
+
self.view.addSubview(navigation_bar_view)
|
47
|
+
|
48
|
+
add_new_step_view(AnimationStrategy::None.new)
|
49
|
+
end
|
50
|
+
|
51
|
+
def reset!
|
52
|
+
@wizard_data = {}
|
53
|
+
@steps_controllers = []
|
54
|
+
@navigation_bar_view.reset!
|
55
|
+
navigation_bar_view.select(@current_step)
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_new_step_view(animation_strategy)
|
59
|
+
@current_view_controller = get_current_step_view_controller
|
60
|
+
self.addChildViewController(@current_view_controller)
|
61
|
+
@content_view.addSubview(@current_view_controller.view)
|
62
|
+
|
63
|
+
@current_view_controller.view.origin = [0,0]
|
64
|
+
animation_strategy.show_view(@current_view_controller.view)
|
65
|
+
navigation_bar_view.select(@current_step)
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_current_step_view_controller
|
69
|
+
@steps_controllers[@current_step] ||= begin
|
70
|
+
new_view_controller = @steps_controllers_classes[@current_step].alloc.init
|
71
|
+
new_view_controller.extend(MotionWizard::ContentController)
|
72
|
+
new_view_controller.wizard_view_controller = self
|
73
|
+
new_view_controller.view.size = @content_view.size
|
74
|
+
new_view_controller
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove_current_step_view(animation_strategy)
|
79
|
+
current_local_view = @current_view_controller
|
80
|
+
animation_strategy.hide_view(@current_view_controller.view) do |view|
|
81
|
+
view.removeFromSuperview
|
82
|
+
current_local_view.removeFromParentViewController
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def number_of_steps
|
87
|
+
@steps_controllers_classes.size
|
88
|
+
end
|
89
|
+
|
90
|
+
def next(data = {})
|
91
|
+
@wizard_data.merge! data
|
92
|
+
@current_step+=1
|
93
|
+
if @current_step >= number_of_steps
|
94
|
+
@current_step = number_of_steps - 1
|
95
|
+
self.finish
|
96
|
+
return
|
97
|
+
end
|
98
|
+
change_step_view(@forward_animation_strategy_class)
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def previous(data = {})
|
103
|
+
@wizard_data.merge! data
|
104
|
+
@current_step-=1
|
105
|
+
return if @current_step < 0
|
106
|
+
change_step_view(@backward_animation_strategy_class)
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def go_to_step(step_number, data = {})
|
111
|
+
@wizard_data.merge! data
|
112
|
+
animation_klass = @current_step > step_number ? @backward_animation_strategy_class : @forward_animation_strategy_class
|
113
|
+
@current_step = step_number
|
114
|
+
change_step_view(animation_klass)
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
def finish(data = {})
|
119
|
+
@wizard_data.merge! data
|
120
|
+
self.when_finished
|
121
|
+
end
|
122
|
+
|
123
|
+
def change_step_view(animation_strategy_klass)
|
124
|
+
animation_strategy = animation_strategy_klass.new
|
125
|
+
remove_current_step_view(animation_strategy)
|
126
|
+
add_new_step_view(animation_strategy)
|
127
|
+
animation_strategy.animate
|
128
|
+
end
|
129
|
+
|
130
|
+
def create_index_item_at(index)
|
131
|
+
index_item = @index_item_view_class.alloc.init
|
132
|
+
index_item.label.text = "%02d" % (index+1)
|
133
|
+
index_item
|
134
|
+
end
|
135
|
+
|
136
|
+
def setup_index_item_at(index_item, index)
|
137
|
+
index_item.label_wrapper.size = index_item.size
|
138
|
+
index_item.label.size = index_item.size
|
139
|
+
end
|
140
|
+
|
141
|
+
def when_finished;end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module MotionWizard
|
2
|
+
module Forwardable
|
3
|
+
def delegate(*methods)
|
4
|
+
to = methods.shift
|
5
|
+
methods.each do |method_name|
|
6
|
+
define_method(method_name) do |*args, &block|
|
7
|
+
self.instance_variable_get(to).send(method_name, *args, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|