sugarcube 0.11.3 → 0.12

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  pkg/
2
2
  *.gem
3
3
  .DS_Store
4
+ /build/
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown - LICENSE
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- sugarcube
1
+ SugarCube
2
2
  =========
3
3
 
4
4
  Some sugar for your cocoa, or your [tea][sweettea].
@@ -8,14 +8,14 @@ About
8
8
 
9
9
  CocoaTouch/iOS is a *verbose* framework. These extensions hope to make
10
10
  development in rubymotion more enjoyable by tacking "UI" methods onto the base
11
- classes (String, Fixnum, Numeric). With sugarcube, you can create a color from an
11
+ classes (String, Fixnum, Numeric). With SugarCube, you can create a color from an
12
12
  integer or symbol, or create a UIFont or UIImage from a string.
13
13
 
14
14
  Some UI classes are opened up as well, like adding the '<<' operator to a UIView
15
15
  instance, instead of view.addSubview(subview), you can use the more idiomatic:
16
16
  view << subview.
17
17
 
18
- The basic idea of sugarcube is to turn some operations on their head. Insead of
18
+ The basic idea of SugarCube is to turn some operations on their head. Insead of
19
19
 
20
20
  UIApplication.sharedApplication.openURL(NSURL.URLWithString(url))
21
21
 
@@ -25,7 +25,7 @@ How about:
25
25
 
26
26
  **DISCLAIMER**
27
27
 
28
- It is possible that you *will not like sugarcube*. That is perfectly fine!
28
+ It is possible that you *will not like SugarCube*. That is perfectly fine!
29
29
  Some people take milk in their coffee, some take sugar. Some crazy maniacs
30
30
  don't even *drink* coffee, if you can imagine that... All I'm saying is: to each
31
31
  their own. You should checkout [BubbleWrap][] for another take on
@@ -45,11 +45,14 @@ Installation
45
45
 
46
46
  gem install sugarcube
47
47
 
48
+ # in Rakefile
49
+ require 'sugarcube'
50
+
48
51
  # or in Gemfile
49
52
  gem 'sugarcube'
50
53
 
51
- # in Rakefile
52
- require 'sugarcube'
54
+ # in terminal
55
+ $ bundle install
53
56
 
54
57
  Examples
55
58
  ========
@@ -114,7 +117,8 @@ Shorthands and hash-like access to the coder/decoder objects.
114
117
  coder['key'] = self.value
115
118
  self.value = decoder['key']
116
119
 
117
- # but if you want to store booleans and such in the MOST compact way:
120
+ # but if you want to store booleans and such (in their C form,
121
+ # which will take up less space I suppose):
118
122
  coder.set('sugarcube_is_neat', toBool:self.is_sugarcube_neat?)
119
123
  self.sugarcube_is_neat = decoder.bool('sugarcube_is_neat')
120
124
 
@@ -160,6 +164,11 @@ recurring events)
160
164
  ignoring the time components. `start_of_day` and `end_of_day` methods help
161
165
  you here. They are akin to `floor` and `ceil`, if you consider the time to
162
166
  be the "floating" component, and the date to be the nearest "integer".
167
+ 4. Formatting is made easier with `NSDate#string_with_style(NSDateStyleConstant or Symbol)`
168
+ and `NSDate#string_with_format(format_string)`. See
169
+ <http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns> for
170
+ the formatters, they take getting used to, coming from `strftime`, but they
171
+ are much more powerful and locale-aware.
163
172
 
164
173
  ```
165
174
  (main)> now = NSDate.new # Time.new is the same thing
@@ -358,6 +367,11 @@ This is the "big daddy". Lots of sugar here...
358
367
  :change.uicontrolevent # => UIControlEventValueChanged
359
368
  :all.uicontrolevent # => UIControlEventAllEvents
360
369
  :blue.uicolor # UIColor.blueColor
370
+
371
+ # these are really handy for custom buttons - touch_start means the finger is inside the button, touch_stop is outside the button or canceled
372
+ :touch_start # => UIControlEventTouchDown | UIControlEventTouchDragEnter
373
+ :touch_stop # => UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchDragExit
374
+
361
375
  # all CSS colors are supported, and alpha
362
376
  # (no "grey"s, only "gray"s, consistent with UIKit, which only provides "grayColor")
363
377
  :firebrick.uicolor(0.25) # => 0xb22222.uicolor(0.25)
@@ -385,16 +399,28 @@ image = "my_image".uiimage
385
399
  image.uicolor # => UIColor.colorWithPatternImage(image)
386
400
  ```
387
401
 
388
- ###### Image Manipulation
402
+ ###### Image Manipulation - VERY handy!
389
403
 
390
404
  ```ruby
391
405
  image.scale_to [37, 37]
392
406
  image.rounded # default: 5 pt radius
393
407
  image.rounded(10)
394
408
 
395
- # these both use UIEdgeInsetsZero (for now)
409
+ image.in_rect([[10, 10], [100, 100]]) # get part of an image
410
+
411
+ image.darken # => good for "pressed" buttons
412
+ image.darken(brightness: -0.5, saturation: -0.2) # these are the defaults
413
+
414
+ image.rotate(:left)
415
+ image.rotate(:right)
416
+ image.rotate(:flip) # 180° - if you have a better name, let me know!
417
+ image.rotate(45.degrees)
418
+
419
+ # default insets are UIEdgeInsetsZero
396
420
  image.tileable
421
+ image.tileable(insets)
397
422
  image.stretchable
423
+ image.stretchable(insets)
398
424
  ```
399
425
 
400
426
  UIAlertView
@@ -530,7 +556,7 @@ UIActivityIndicatorView.gray
530
556
  -----------
531
557
 
532
558
  Inspired by [BubbleWrap's][BubbleWrap] `when` method, but I prefer jQuery-style
533
- verbs and sugarcube symbols.
559
+ verbs and SugarCube symbols.
534
560
 
535
561
  ```ruby
536
562
  button = UIButton.alloc.initWithFrame([0, 0, 10, 10])
@@ -715,13 +741,13 @@ date2.same_day? date1
715
741
  :key.get_default # => NSUserDefaults.standardUserDefaults.objectForKey(:key)
716
742
  ```
717
743
 
718
- This is strange, and backwards, which is just sugarcube's style. But there is
744
+ This is strange, and backwards, which is just SugarCube's style. But there is
719
745
  one advantage to doing it this way. Compare these two snippets:
720
746
 
721
747
  ```ruby
722
748
  # BubbleWrap
723
749
  App::Persistance[:test] = { my: 'test' }
724
- # sugarcube
750
+ # SugarCube
725
751
  :test.set_default { my: 'test' }
726
752
  # k, BubbleWrap looks better
727
753
 
@@ -743,19 +769,26 @@ test[:my] = 'new'
743
769
  CoreGraphics
744
770
  --------------
745
771
 
746
- ###### Is it `CGMakeRect` or `CGRectMake`?
772
+ ###### Is it `CGMakeRect` or `CGRectMake`? What arguments does `CGRect.new` take?
747
773
 
748
- Instead, just use `Rect`, `Size` and `Point`. They will happily convert most
749
- sensible arguments into a `Rect/Size/Point`, which can be treated as a `CGRect`
750
- object OR as an `Array` (woah).
774
+ Instead, just use the coercion methods `Rect()`, `Size()` and `Point()`. They
775
+ will happily convert most sensible (and some non-sensible) arguments into a
776
+ `CGRect/CGSize/CGPoint` struct. For more CoreGraphics additions, you should use
777
+ [geomotion][] by [Clay Allsopp][]. It adds methods to `CGRect`, `CGPoint`, and
778
+ `CGSize` to make these structures more rubyesque (these methods used to be part
779
+ of SugarCube, but were removed in an attempt to decrease the amount of
780
+ duplicated code).
751
781
 
752
- These are namespaced in `SugarCube::CoreGraphics` module, but I recommend you
753
- `include SugarCube::CoreGraphics` in app_delegate.rb.
782
+ [geomotion]: https://github.com/clayallsopp
783
+ [Clay Allsopp]: https://github.com/clayallsopp/geomotion
784
+
785
+ These are namespaced in the `SugarCube::CoreGraphics` module, but I recommend
786
+ you `include SugarCube::CoreGraphics` in app_delegate.rb.
754
787
 
755
788
  ```ruby
756
- f = Rect(view.frame) # converts a CGRect into a Rect
757
- o = Point(view.frame.origin) # converts a CGPoint into a Point
758
- s = Size(view.frame.size) # converts a CGSize into a Size
789
+ f = Rect(view.frame) # the identity function - returns a copy of the CGRect
790
+ o = Point(view.frame.origin) # returns a copy of CGPoint
791
+ s = Size(view.frame.size) # returns a copy of CGSize
759
792
 
760
793
  # lots of other conversions are possible.
761
794
  # a UIView or CALayer => view.frame
@@ -763,103 +796,19 @@ f = Rect(view)
763
796
  # 4 numbers
764
797
  f = Rect(x, y, w, h)
765
798
  # or two arrays
799
+ f = Rect([x, y], [w, h])
800
+ # one array
801
+ f = Rect([[x, y], [w, h]])
802
+ f = Rect([x, y, w, h])
803
+ # a CGPoint and CGSize
766
804
  p = Point(x, y) # or just [x, y] works, too
767
805
  s = Size(w, h) # again, [w, h] is fine
768
806
  f = Rect(p, s)
769
- # like I said, a straight-up array of nested arrays is fine, too.
770
- f = Rect([[x, y], [w, h]])
771
- ```
772
-
773
- ###### CG{Rect,Point,Size} is a *real boy*!
774
-
775
- These methods get defined in a module (`SugarCube::CG{Rect,Size,Point}Extensions`),
776
- and included in `CGRect` *and* `Rect`. The idea is that you do not have to
777
- distinguish between the two objects.
778
-
779
- These methods all use the methods as described in [CGGeometry Reference][], e.g.
780
- `CGRectContainsPoint`, `CGRectIntersectsRect`, etc.
781
-
782
- ```ruby
783
- # intersection / contains
784
- Point(0, 0).intersects?(Rect(-1, -1, 2, 2)) # => true
785
- # if a Point intersects a Rect, the Rect intersects the Point, right?
786
- Rect(-1, -1, 2, 2).intersects? Point(0, 0) # => true
787
-
788
- # CGRect and the gang are real Ruby objects. Let's treat 'em that way!
789
- view.frame.contains? Point(10, 10) # in this case, contains? and intersects? are synonyms
790
- view.frame.intersects? Rect(0, 0, 10, 10) # <= but this one
791
- view.frame.contains? Rect(0, 0, 10, 10) # <= and this one are different.
792
-
793
- # CGRect has factory methods for CGRectEmpty, CGRectNull, and - KINDA - CGRectInfinite
794
- # BUT, there is a bug (?) right now where CGRectIsInfinite(CGRectInfinite) returns false.
795
- # so instead, I've built my own infinite? method that checks for the special "Infinite" value
796
- > CGRect.infinite
797
- => [[0, 0], [Infinity, Infinity]]
798
- > CGRect.infinite.infinite?
799
- => true
800
- > CGRect.null
801
- => [[Infinity, Infinity], [0.0, 0.0]]
802
- > CGRect.null.null?
803
- => true
804
- > CGRect.empty
805
- => [[0.0, 0.0], [0.0, 0.0]]
806
- > CGRect.empty.empty?
807
- => true
808
- ```
809
-
810
- A lot of the methods in CGGeometry Reference are available as instance methods
811
-
812
- ```ruby
813
- view.frame.left # => CGRectGetMinX(view.frame)
814
- view.frame.right # => CGRectGetMaxX(view.frame)
815
- view.frame.top # => CGRectGetMinY(view.frame)
816
- view.frame.bottom # => CGRectGetMaxY(view.frame)
817
- view.frame.width # => CGRectGetWidth(view.frame)
818
- view.frame.height # => CGRectGetHeight(view.frame)
819
- view.frame.center # => Point(CGRectGetMidX(view.frame), CGRectGetMidY(view.frame))
820
-
821
- view.frame.intersection(another_rect) # => CGRectIntersection(view.frame, another_rect)
822
- view.frame + another_rect # => CGRectUnion(view.frame, another_rect)
823
- view.frame + a_point # => CGRectOffset(view.frame, a_point.x, a_point.y)
824
- view.frame + a_offset # => CGRectOffset(view.frame, a_offset.horizontal, a_offset.vertical)
825
- view.frame + edgeinsets # => UIEdgeInsetsInsetRect(view.frame, edgeinsets)
826
- view.frame + a_size # => CGRectInset(view.frame, -a_size.width, -a_size.height)
827
- # Adding a size to a view keeps the view's CENTER in the same place, but
828
- # increases its size by `size.width,size.height`. it's the same as using
829
- # UIEdgeInsets with top == bottom, and left == right
830
- > Rect(0, 0, 10, 10).center
831
- => Point(5.0, 5.0) # note the center
832
- > Rect(0, 0, 10, 10) + Size(10, 10)
833
- => Rect([-10.0, -10.0],{30.0 × 30.0}) # origin and size changed, but...
834
- > (Rect(0, 0, 10, 10) + Size(10, 10)).center
835
- => Point(5.0, 5.0)
836
- # See? It's bigger, but the center hasn't moved.
807
+ # any combination of the two
808
+ f = Rect(p, [w, h])
809
+ f = Rect([x, y], s)
837
810
  ```
838
811
 
839
- `to_hash/from_hash`, and notice here that I used `inspect`, to show that it is a
840
- little more readable.
841
-
842
- **NOTE** As of today, Aug. 25, 2012, rubymotion v1.22, the `inspect` method in SugarCube is not
843
- being called. I think this is a bug... this worked before!
844
-
845
- ```ruby
846
- > Rect(0, 0, 10, 10).to_hash
847
- => {"Width"=>10.0, "Height"=>10.0, "Y"=>0.0, "X"=>0.0}
848
- > puts CGRect.from_hash(Rect(0, 0, 1, 1).to_hash).inspect
849
- CGRect([0.0, 0.0],{1.0 × 1.0})
850
- ```
851
-
852
- `to_s/from_s`, which rely on `NSStringFromCGRect/CGRectFromString` (et. al.)
853
-
854
- ```ruby
855
- > Rect(0, 0, 10, 10).to_s
856
- => "{{0, 0}, {10, 10}}"
857
- > puts CGRect.from_s Rect(0, 0, 10, 10).to_s
858
- {{0, 0}, {10, 10}}
859
- ```
860
-
861
- [CGGeometry Reference]: https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html
862
-
863
812
  CoreLocation
864
813
  --------------
865
814
 
@@ -1126,3 +1075,45 @@ true.blank? # => false
1126
1075
  ['a'].blank? # => false
1127
1076
  {a: 'a'}.blank? # => false
1128
1077
  ```
1078
+
1079
+ Gestures
1080
+ --------
1081
+
1082
+ Sugarcube's gesture support is very similar to BubbleWrap's, and it's entirely
1083
+ possible that the two will be merged into one thing. But SugarCube is all about
1084
+ extending base classes, whereas BubbleWrap tends to add *new* classes to do the
1085
+ heavy lifting. Plus the options you pass to SugarCube are very different, and
1086
+ the prefix is "on" instead of "when" (e.g. "on_pan" instead of "when_panned")
1087
+
1088
+ Gestures are an "opt-in" extension. In your Rakefile, add
1089
+ `require 'sugarcube-gestures'`.
1090
+
1091
+ ```ruby
1092
+ require 'sugarcube-gestures'
1093
+
1094
+ view.on_pan { |gesture|
1095
+ location = gesture.view.locationInView(view)
1096
+ }
1097
+
1098
+ # other gesture methods, with common options:
1099
+ view.on_tap # use system defaults
1100
+ view.on_tap(1) # number of taps
1101
+ view.on_tap(taps: 1, fingers: 1) # number of taps and number of fingers
1102
+
1103
+ view.on_pinch # no options
1104
+ view.on_rotate # no options
1105
+
1106
+ view.on_swipe # use system defaults
1107
+ view.on_swipe :left
1108
+ view.on_swipe(direction: :left, fingers: 1)
1109
+ view.on_swipe(direction: UISwipeGestureRecognizerDirectionLeft, fingers: 1)
1110
+
1111
+ view.on_pan # use system defaults
1112
+ view.on_pan(2) # minimum and maximum fingers required
1113
+ view.on_pan(fingers: 2)
1114
+ view.on_pan(min_fingers: 2, max_fingers: 3)
1115
+
1116
+ view.on_press # use system defaults
1117
+ view.on_press(1.5) # duration
1118
+ view.on_press(duration: 1.5, taps: 1, fingers: 1)
1119
+ ```
data/Rakefile CHANGED
@@ -1 +1,3 @@
1
- require 'bundler/gem_tasks'
1
+ $:.unshift('/Library/RubyMotion/lib')
2
+ require 'motion/project'
3
+ require './lib/sugarcube'
@@ -0,0 +1,4 @@
1
+ include SugarCube::CoreGraphics
2
+
3
+ class AppDelegate
4
+ end
@@ -0,0 +1,23 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "The sugarcube gem must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+
6
+ Motion::Project::App.setup do |app|
7
+ # scans app.files until it finds app/ (the default)
8
+ # if found, it inserts just before those files, otherwise it will insert to
9
+ # the end of the list
10
+ insert_point = 0
11
+ app.files.each_index do |index|
12
+ file = app.files[index]
13
+ if file =~ /^(?:\.\/)?app\//
14
+ # found app/, so stop looking
15
+ break
16
+ end
17
+ insert_point = index + 1
18
+ end
19
+
20
+ Dir.glob(File.join(File.dirname(__FILE__), 'sugarcube-gestures/**/*.rb')).reverse.each do |file|
21
+ app.files.insert(insert_point, file)
22
+ end
23
+ end
@@ -0,0 +1,134 @@
1
+ # BubbleWrap has these same methods, but the logic and options are a little
2
+ # different. In the spirit of open source, I am blatantly copying their code,
3
+ # changing it to suit my needs, and offering it here
4
+ class UIView
5
+
6
+ # a generic gesture adder, but using sugarcube_add_gesture, which handles the block
7
+ def on_gesture(klass, options={}, &proc)
8
+ recognizer = klass.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
9
+ options.each do |method, value|
10
+ recognizer.send(method, value)
11
+ end
12
+ sugarcube_add_gesture(proc, recognizer)
13
+ end
14
+
15
+ def on_tap(taps_or_options=nil, &proc)
16
+ taps = nil
17
+ fingers = nil
18
+
19
+ if taps_or_options
20
+ if taps_or_options.is_a? Hash
21
+ taps = taps_or_options[:taps] || taps
22
+ fingers = taps_or_options[:fingers] || fingers
23
+ else
24
+ taps = taps_or_options
25
+ end
26
+ end
27
+
28
+ recognizer = UITapGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
29
+ recognizer.numberOfTapsRequired = taps if taps
30
+ recognizer.numberOfTouchesRequired = fingers if fingers
31
+ sugarcube_add_gesture(proc, recognizer)
32
+ end
33
+
34
+ def on_pinch(&proc)
35
+ recognizer = UIPinchGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
36
+ sugarcube_add_gesture(proc, recognizer)
37
+ end
38
+
39
+ def on_rotate(&proc)
40
+ recognizer = UIRotationGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
41
+ sugarcube_add_gesture(proc, recognizer)
42
+ end
43
+
44
+ def on_swipe(direction_or_options=nil, &proc)
45
+ direction = UISwipeGestureRecognizerDirectionRight
46
+ fingers = nil
47
+
48
+ if direction_or_options
49
+ if direction_or_options.is_a? Hash
50
+ direction = direction_or_options[:direction] || direction
51
+ fingers = direction_or_options[:fingers] || fingers
52
+ else
53
+ direction = direction_or_options
54
+ end
55
+ end
56
+
57
+ case direction
58
+ when :left
59
+ direction = UISwipeGestureRecognizerDirectionLeft
60
+ when :right
61
+ direction = UISwipeGestureRecognizerDirectionRight
62
+ when :up
63
+ direction = UISwipeGestureRecognizerDirectionUp
64
+ when :down
65
+ direction = UISwipeGestureRecognizerDirectionDown
66
+ end
67
+
68
+ recognizer = UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
69
+ recognizer.direction = direction if direction
70
+ recognizer.numberOfTouchesRequired = fingers if fingers
71
+ sugarcube_add_gesture(proc, recognizer)
72
+ end
73
+
74
+ def on_pan(fingers_or_options=nil, &proc)
75
+ fingers = nil
76
+ min_fingers = nil
77
+ max_fingers = nil
78
+
79
+ if fingers_or_options
80
+ if fingers_or_options.is_a? Hash
81
+ fingers = fingers_or_options[:fingers] || fingers
82
+ min_fingers = fingers_or_options[:min_fingers] || min_fingers
83
+ max_fingers = fingers_or_options[:max_fingers] || max_fingers else
84
+ fingers = fingers_or_options
85
+ end
86
+ end
87
+
88
+ recognizer = UIPanGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
89
+ sugarcube_add_gesture(proc, recognizer)
90
+ end
91
+
92
+ def on_press(duration_or_options=nil, &proc)
93
+ duration = nil
94
+ taps = nil
95
+ fingers = nil
96
+
97
+ if duration_or_options
98
+ if duration_or_options.is_a? Hash
99
+ duration = duration_or_options[:duration] || duration
100
+ taps = duration_or_options[:taps] || taps
101
+ fingers = duration_or_options[:fingers] || fingers
102
+ else
103
+ duration = duration_or_options
104
+ end
105
+ end
106
+
107
+ recognizer = UILongPressGestureRecognizer.alloc.initWithTarget(self, action:'sugarcube_handle_gesture:')
108
+ recognizer.minimumPressDuration = duration if duration
109
+ recognizer.numberOfTapsRequired = taps if taps
110
+ recognizer.numberOfTouchesRequired = fingers if fingers
111
+ sugarcube_add_gesture(proc, recognizer)
112
+ end
113
+
114
+ private
115
+ def sugarcube_handle_gesture(recognizer)
116
+ handler = @sugarcube_recognizers[recognizer]
117
+ if handler.arity == 0
118
+ handler.call
119
+ else
120
+ handler.call(recognizer)
121
+ end
122
+ end
123
+
124
+ # Adds the recognizer and keeps a strong reference to the Proc object.
125
+ def sugarcube_add_gesture(proc, recognizer)
126
+ self.addGestureRecognizer(recognizer)
127
+
128
+ @sugarcube_recognizers = {} unless @sugarcube_recognizers
129
+ @sugarcube_recognizers[recognizer] = proc
130
+
131
+ recognizer
132
+ end
133
+
134
+ end