sugarcube 0.18.12 → 0.18.16

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -727,9 +727,10 @@ view.fade_out { |completed|
727
727
  }
728
728
 
729
729
  # fade_out options
730
- view.fade_out(0.5, delay: 0,
731
- options: UIViewAnimationOptionCurveLinear,
732
- opacity: 0.5) {
730
+ view.fade_out(duration: 0.5,
731
+ delay: 0,
732
+ options: UIViewAnimationOptionCurveLinear,
733
+ opacity: 0.5) {
733
734
  view.removeFromSuperview
734
735
  }
735
736
 
@@ -737,6 +738,7 @@ view.move_to([0, 100]) # move to position 0, 100
737
738
  view.delta_to([0, 100]) # move over 0, down 100, from current position
738
739
 
739
740
  view.rotate_to Math::PI # rotate view upside down
741
+ view.rotate 45.degrees # rotate *an additional* 45 degrees
740
742
  view.rotate_to(duration: 0.5, angle: 45.degrees) # using options
741
743
 
742
744
  view.slide :left # slides the entire view one "page" to the left, right, up, or down
@@ -774,43 +776,77 @@ view.slide(:left, 20) {
774
776
  }
775
777
  ```
776
778
 
777
- Those be some gnarly callbacks. You can write this as a chain, but we need to
778
- tell the `slide` method to go ahead and run immediately (don't use an
779
- animation). When you create an animation chain, the block you pass to each
780
- iteration is the block that gets passed to `UIView##animateWithDuration(...)`.
781
- If you try and run animations *within* that block they will not get queued up
782
- properly.
779
+ Those be some gnarly callbacks. You can write this as a chain instead!
783
780
 
784
- The easiest way to handle this:
781
+ ```ruby
782
+ UIView.animation_chain {
783
+ view.slide(:left, 20)
784
+ }.and_then {
785
+ view.slide(:up, 20)
786
+ }.and_then {
787
+ view.slide(:right, 20)
788
+ }.and_then {
789
+ view.slide(:down, 20)
790
+ }.and_then {
791
+ view.fade_out
792
+ }.start
793
+ ```
785
794
 
786
- 1) don't use SugarCube animation methods, e.g. assign the frame manually (yeah, right!)
787
- 2) use the `duration: 0` option, which runs the animation without setting calling
788
- `animateWithDuration()`.
789
- 3) For clarity (and some future-proofing, in case any of this rigamarole changes
790
- in the future), you can use `chain: true`.
795
+ Behind the scenes, any calls to a SugarCube animate method (`slide`, `fade`,
796
+ `rotate`) will be setup to run *immediately* instead of in a
797
+ `UIView#animateWithDuration(...)` block. You can also do multiple animations
798
+ within that block, as long as no two animations affect the same property:
791
799
 
792
800
  ```ruby
793
801
  UIView.animation_chain {
794
- view.slide(:left, duration:0)
802
+ view.slide(:left, 20)
803
+ view.rotate(90.degrees)
795
804
  }.and_then {
796
- view.slide(:up, chain:true) # sets duration:0, delay:0
805
+ view.slide(:up, 20)
806
+ view.rotate(90.degrees)
797
807
  }.and_then {
798
- view.fade_out # in case you were wondering, the very LAST animation CAN be
799
- }.start # called normally.
808
+ view.slide(:right, 20)
809
+ view.rotate(90.degrees)
810
+ }.and_then {
811
+ view.slide(:down, 20)
812
+ view.rotate(90.degrees)
813
+ }.and_then {
814
+ view.fade_out
815
+ view.rotate_to(0.degrees)
816
+ }.start
800
817
  ```
801
818
 
802
819
  Chains can also be written like this:
803
820
 
804
821
  ```ruby
805
822
  chain = UIView.animation_chain
806
- chain << proc { view.slide(:left, 20) }
807
- chain << proc { view.slide(:up, 20) }
808
- chain << proc { view.slide(:right, 20) }
809
- chain << proc { view.slide(:down, 20) }
823
+ chain << proc { view.slide(:left, 20, chain: true) }
824
+ chain << proc { view.slide(:up, 20, chain: true) }
825
+ chain << proc { view.slide(:right, 20, chain: true) }
826
+ chain << proc { view.slide(:down, 20, chain: true) }
810
827
  chain << proc { view.fade_out }
811
828
  chain.start
812
829
  ```
813
830
 
831
+ **AND** chains can be looped! Either number of times, or call `stop` on the
832
+ chain.
833
+
834
+ ```ruby
835
+ chain = UIView.animation_chain {
836
+ view.slide(:left, 20)
837
+ }.and_then {
838
+ view.slide(:right, 20)
839
+ }.loop # loop forever
840
+ 2.seconds.later { chain.stop } # the animation will complete, but not loop again
841
+ chain.loop(10) # would loop 10 times
842
+
843
+ # if you're impatient
844
+ chain.abort
845
+ # will stop the animation at the end of whatever block it is in, so it could be
846
+ # in a strange position, depending on where in the chain it is. better to call
847
+ # `stop`
848
+ ```
849
+
814
850
  ##### View factories
815
851
 
816
852
  ###### UIButton
@@ -1,5 +1,6 @@
1
1
  module SugarCube
2
2
  class AnimationChain
3
+
3
4
  class << self
4
5
 
5
6
  def chains
@@ -7,17 +8,17 @@ module SugarCube
7
8
  end
8
9
 
9
10
  def start_chain(chain)
10
- chains << chain
11
+ chains << chain unless chains.include?(chain)
11
12
  end
12
13
 
13
14
  def stop_chain(chain)
14
- chains ||= []
15
- @chains.delete(chain)
15
+ chains.delete(chain)
16
16
  end
17
17
 
18
18
  end
19
19
 
20
20
  def initialize
21
+ raise "animation chains cannot be nested" if Thread.current[:sugarcube_chaining]
21
22
  @blocks = []
22
23
  end
23
24
 
@@ -44,21 +45,57 @@ module SugarCube
44
45
 
45
46
  options, block = @blocks[@block_index]
46
47
  @after_block = ->(completed){
47
- self.do_next || AnimationChain.stop_chain(self)
48
+ if @abort || ! self.do_next
49
+ @running = false
50
+ if @loop
51
+ start
52
+ else
53
+ AnimationChain.stop_chain(self)
54
+ end
55
+ end
48
56
  }
49
57
  options[:after] = @after_block
50
58
 
51
59
  UIView.animate(options) {
60
+ Thread.current[:sugarcube_chaining] = true
52
61
  block.call
62
+ Thread.current[:sugarcube_chaining] = nil
53
63
  @block_index += 1
54
64
  }
55
65
  true
56
66
  end
57
67
 
58
68
  def start
69
+ return if @running
59
70
  AnimationChain.start_chain(self)
71
+ @running = true
72
+ @abort = nil
60
73
  @block_index = 0
74
+ if Fixnum === @loop
75
+ @loop -= 1
76
+ @loop = nil if @loop == 0
77
+ end
61
78
  do_next
79
+ return self
80
+ end
81
+
82
+ # @param times [Fixnum,nil] number of times to loop, or any other truthy value to loop forever
83
+ def loop(times=true)
84
+ @loop = times
85
+ start
86
+ end
87
+
88
+ # Cancels a loop, but lets the chain finish
89
+ def stop
90
+ @loop = nil
91
+ end
92
+
93
+ # stops the animation immediately
94
+ def abort
95
+ return unless @running
96
+ @loop = nil
97
+ @abort = true
98
+ @running = false
62
99
  end
63
100
 
64
101
  end
@@ -34,9 +34,10 @@ class UIView
34
34
 
35
35
  # chain: true is used inside animation_chain blocks to prevent some weird
36
36
  # animation errors (nested animations do not delay/queue as you'd expect)
37
- if options[:chain]
37
+ if options[:chain] || Thread.current[:sugarcube_chaining]
38
38
  duration = 0
39
39
  delay = 0
40
+ raise "Completion blocks cannot be used within an animation_chain block" if options[:after]
40
41
  end
41
42
 
42
43
  after_animations = options[:after]
@@ -56,7 +57,7 @@ class UIView
56
57
  else
57
58
  UIView.animateWithDuration( duration,
58
59
  delay: delay,
59
- options: options[:options] || UIViewAnimationOptionCurveEaseInOut,
60
+ options: options[:options] || (UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionBeginFromCurrentState),
60
61
  animations: animations,
61
62
  completion: after_adjusted
62
63
  )
@@ -225,6 +226,8 @@ class UIView
225
226
  self
226
227
  end
227
228
 
229
+ # Changes the current rotation to `new_angle`
230
+ # (`rotate` rotates relative to the current rotation)
228
231
  def rotate_to(options={}, &after)
229
232
  if options.is_a? Numeric
230
233
  options = { angle: options }
@@ -237,6 +240,21 @@ class UIView
237
240
  }
238
241
  end
239
242
 
243
+ # Changes the current rotation by `new_angle`
244
+ # (`rotate` rotates to a specific angle)
245
+ def rotate(options={}, &after)
246
+ if options.is_a? Numeric
247
+ new_angle = options
248
+ options = {}
249
+ else
250
+ new_angle = options[:angle]
251
+ end
252
+
253
+ old_angle = valueForKeyPath('layer.transform.rotation.z')
254
+ options[:angle] = old_angle + new_angle
255
+ rotate_to(options, &after)
256
+ end
257
+
240
258
  def slide(direction, options={}, more_options=nil, &after)
241
259
  if options.is_a? Numeric
242
260
  size = options
@@ -312,7 +330,7 @@ class UIView
312
330
  end
313
331
 
314
332
  options[:duration] ||= default_duration
315
- options[:options] ||= UIViewAnimationOptionCurveEaseIn
333
+ options[:options] ||= UIViewAnimationOptionCurveEaseIn|UIViewAnimationOptionBeginFromCurrentState
316
334
  reset_transform = self.transform
317
335
  reset_after = ->(finished) {
318
336
  self.transform = reset_transform
@@ -1,3 +1,3 @@
1
1
  module SugarCube
2
- Version = '0.18.12'
2
+ Version = '0.18.16'
3
3
  end
@@ -1,10 +1,6 @@
1
1
  describe "SugarCube::AnimationChain" do
2
2
  tests SugarCube::AnimationChainController
3
3
 
4
- it "should have a view" do
5
- controller.view.should != nil
6
- end
7
-
8
4
  it "should support chains" do
9
5
  SugarCube::AnimationChain.chains.length.should == 0
10
6
  @variable_a = nil
@@ -73,4 +69,72 @@ describe "SugarCube::AnimationChain" do
73
69
  }
74
70
  end
75
71
 
72
+ it "should support looping" do
73
+ SugarCube::AnimationChain.chains.length.should == 0
74
+ @variable_a = 0
75
+ @num_loops = 2
76
+ UIView.animation_chain(duration:0.1){
77
+ @variable_a += 1
78
+ }.loop(@num_loops)
79
+ SugarCube::AnimationChain.chains.length.should == 1
80
+
81
+ wait 0.3 {
82
+ @variable_a.should == @num_loops
83
+ SugarCube::AnimationChain.chains.length.should == 0
84
+ }
85
+ end
86
+
87
+ it "should support stopping" do
88
+ SugarCube::AnimationChain.chains.length.should == 0
89
+ @variable_a = 0
90
+ @num_loops = 0
91
+ @chain = UIView.animation_chain(duration:0.1){
92
+ @variable_a += 1
93
+ f = controller.view.frame
94
+ f.origin.x -= 20
95
+ controller.view.frame = f
96
+ }.loop
97
+ SugarCube::AnimationChain.chains.length.should == 1
98
+
99
+ wait 0.2 {
100
+ SugarCube::AnimationChain.chains.length.should == 1
101
+ @variable_a.should > 1
102
+ @chain.stop
103
+ }
104
+
105
+ wait 0.4 {
106
+ SugarCube::AnimationChain.chains.length.should == 0
107
+ @variable_a.should < 4
108
+ }
109
+ end
110
+
111
+ it "should support aborting" do
112
+ SugarCube::AnimationChain.chains.length.should == 0
113
+ @variable_a = 0
114
+ @num_loops = 0
115
+ @chain = UIView.animation_chain(duration:0.1){
116
+ @variable_a += 1
117
+ f = controller.view.frame
118
+ f.origin.x -= 20
119
+ controller.view.frame = f
120
+ }.loop
121
+ SugarCube::AnimationChain.chains.length.should == 1
122
+
123
+ wait 0.21 {
124
+ SugarCube::AnimationChain.chains.length.should == 1
125
+ @variable_a.should == 2
126
+ }
127
+
128
+ wait 0.31 {
129
+ @chain.abort
130
+ SugarCube::AnimationChain.chains.length.should == 0
131
+ @variable_a.should > 1
132
+ @variable_a.should < 4
133
+ @num_loops = @variable_a
134
+ }
135
+ wait 0.51 {
136
+ @variable_a.should == @num_loops
137
+ }
138
+ end
139
+
76
140
  end
@@ -8,12 +8,20 @@ describe "UIView animation methods" do
8
8
  end
9
9
 
10
10
  it 'should rotate 45 degrees' do
11
- angle = 45*Math::PI/180
11
+ angle = 45.degrees
12
12
  @view.rotate_to(angle)
13
13
  current_angle = Math.atan2(@view.transform.b, @view.transform.a)
14
14
  current_angle.should == angle
15
15
  end
16
16
 
17
+ it 'should rotate 45 degrees and then 45 degrees again' do
18
+ angle = 45.degrees
19
+ @view.rotate_to(angle)
20
+ @view.rotate(angle)
21
+ current_angle = Math.atan2(@view.transform.b, @view.transform.a)
22
+ current_angle.should == 90.degrees
23
+ end
24
+
17
25
  it 'should animate anything' do
18
26
  UIView.animate {
19
27
  @view.frame = [[0, 0], [0, 0]]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sugarcube
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.12
4
+ version: 0.18.16
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-02-22 00:00:00.000000000 Z
16
+ date: 2013-02-26 00:00:00.000000000 Z
17
17
  dependencies: []
18
18
  description: ! '== Description
19
19
 
@@ -123,6 +123,7 @@ files:
123
123
  - runtests
124
124
  - spec/568_spec.rb
125
125
  - spec/activesupport_spec.rb
126
+ - spec/animation_chain_spec.rb
126
127
  - spec/calayer_spec.rb
127
128
  - spec/core_graphics_spec.rb
128
129
  - spec/core_location_spec.rb
@@ -147,7 +148,6 @@ files:
147
148
  - spec/uicolor_spec.rb
148
149
  - spec/uiimage_color_at_spec.rb
149
150
  - spec/uiimage_scale_spec.rb
150
- - spec/uiview_animation_chain_spec.rb
151
151
  - spec/uiview_animation_spec.rb
152
152
  - spec/uiview_attr_updates_spec.rb
153
153
  - spec/uiview_spec.rb
@@ -181,6 +181,7 @@ summary: Extensions for Ruby to make Rubymotion development more enjoyable, and
181
181
  test_files:
182
182
  - spec/568_spec.rb
183
183
  - spec/activesupport_spec.rb
184
+ - spec/animation_chain_spec.rb
184
185
  - spec/calayer_spec.rb
185
186
  - spec/core_graphics_spec.rb
186
187
  - spec/core_location_spec.rb
@@ -205,9 +206,7 @@ test_files:
205
206
  - spec/uicolor_spec.rb
206
207
  - spec/uiimage_color_at_spec.rb
207
208
  - spec/uiimage_scale_spec.rb
208
- - spec/uiview_animation_chain_spec.rb
209
209
  - spec/uiview_animation_spec.rb
210
210
  - spec/uiview_attr_updates_spec.rb
211
211
  - spec/uiview_spec.rb
212
212
  - spec/unholy_spec.rb
213
- has_rdoc: