sugarcube 1.1.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.md +73 -10
- data/lib/sugarcube-animations/caanimation.rb +31 -0
- data/lib/sugarcube-animations/calayer.rb +7 -0
- data/lib/sugarcube-animations/uiview.rb +59 -34
- data/lib/sugarcube-attributedstring/nsattributedstring.rb +7 -0
- data/lib/sugarcube-color/nsstring.rb +1 -1
- data/lib/sugarcube-color/symbol.rb +2 -2
- data/lib/sugarcube-color/uicolor.rb +56 -10
- data/lib/sugarcube-corelocation/core_location.rb +12 -3
- data/lib/sugarcube-factories/uiactionsheet.rb +5 -4
- data/lib/sugarcube-factories/uialertview.rb +4 -5
- data/lib/sugarcube-image/ciimage.rb +2 -3
- data/lib/sugarcube-image/uiimage.rb +245 -97
- data/lib/sugarcube-nsdate/date_parser.rb +14 -12
- data/lib/sugarcube-nsdate/nsstring.rb +5 -0
- data/lib/sugarcube-nsdate/{fixnum.rb → numeric.rb} +12 -3
- data/lib/sugarcube-numbers/numeric.rb +7 -0
- data/lib/sugarcube-to_s/uilabel.rb +1 -1
- data/lib/sugarcube-to_s/uiview.rb +1 -2
- data/lib/sugarcube-uikit/uiview.rb +75 -5
- data/lib/sugarcube/version.rb +1 -1
- data/spec/nsattributedstring_spec.rb +4 -0
- data/spec/nsstring_files_spec.rb +18 -15
- data/spec/uiactionsheet_spec.rb +40 -2
- data/spec/uialertview_spec.rb +10 -3
- data/spec/uicolor_spec.rb +39 -0
- data/spec/uiimage_spec.rb +471 -0
- data/spec/uiview_spec.rb +90 -22
- metadata +9 -3
@@ -1,16 +1,18 @@
|
|
1
1
|
module SugarCube
|
2
2
|
module DateParser
|
3
|
+
module_function
|
4
|
+
|
3
5
|
# Parse a date string: E.g.:
|
4
6
|
#
|
5
7
|
# SugarCube::DateParser.parse_date "There is a date in here tomorrow at 9:00 AM"
|
6
8
|
#
|
7
9
|
# => 2013-02-20 09:00:00 -0800
|
8
|
-
def
|
10
|
+
def parse_date(date_string)
|
9
11
|
result = sugarcube_detect(date_string).first
|
10
12
|
if result
|
11
13
|
return result.date
|
12
14
|
else
|
13
|
-
return
|
15
|
+
return iso8601(date_string)
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
@@ -20,7 +22,7 @@ module SugarCube
|
|
20
22
|
#
|
21
23
|
# Caveat: This is implemented per Apple documentation. I've never really
|
22
24
|
# seen it work.
|
23
|
-
def
|
25
|
+
def parse_time_zone(date_string)
|
24
26
|
result = sugarcube_detect(date_string).first
|
25
27
|
result && result.timeZone
|
26
28
|
end
|
@@ -32,23 +34,17 @@ module SugarCube
|
|
32
34
|
# => 21600.0
|
33
35
|
#
|
34
36
|
# Divide by 3600.0 to get number of hours duration.
|
35
|
-
def
|
37
|
+
def parse_duration(date_string)
|
36
38
|
result = sugarcube_detect(date_string).first
|
37
39
|
result && result.send(:duration)
|
38
40
|
end
|
39
41
|
|
40
42
|
# Parse a date into a raw match array for further processing
|
41
|
-
def
|
43
|
+
def match(date_string)
|
42
44
|
sugarcube_detect(date_string)
|
43
45
|
end
|
44
46
|
|
45
|
-
|
46
|
-
def self.sugarcube_detect(date_string)
|
47
|
-
@@sugarcube_detector ||= NSDataDetector.dataDetectorWithTypes(NSTextCheckingTypeDate, error:Pointer.new(:object))
|
48
|
-
return @@sugarcube_detector.matchesInString(date_string, options:0, range:NSMakeRange(0, date_string.length))
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.sugarcube_iso8601(date_string)
|
47
|
+
def iso8601(date_string)
|
52
48
|
@@sugarcube_iso_detectors ||= [
|
53
49
|
"yyyy-MM-dd'T'HH:mm:ss",
|
54
50
|
"yyyy-MM-dd'T'HH:mm:ssZ",
|
@@ -63,6 +59,12 @@ module SugarCube
|
|
63
59
|
return @@sugarcube_iso_detectors.inject(nil) { |date, formatter| date || formatter.dateFromString(date_string) }
|
64
60
|
end
|
65
61
|
|
62
|
+
def sugarcube_detect(date_string)
|
63
|
+
@@sugarcube_detector ||= NSDataDetector.dataDetectorWithTypes(NSTextCheckingTypeDate, error:Pointer.new(:object))
|
64
|
+
return @@sugarcube_detector.matchesInString(date_string, options:0, range:NSMakeRange(0, date_string.length))
|
65
|
+
end
|
66
|
+
private :sugarcube_detect
|
67
|
+
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
class
|
1
|
+
class Numeric
|
2
2
|
|
3
|
-
def
|
4
|
-
|
3
|
+
def nsdate
|
4
|
+
NSDate.dateWithTimeIntervalSince1970(self)
|
5
5
|
end
|
6
6
|
|
7
7
|
def before(date)
|
@@ -21,3 +21,12 @@ class Fixnum
|
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
24
|
+
|
25
|
+
|
26
|
+
class Fixnum
|
27
|
+
|
28
|
+
def nstimezone
|
29
|
+
NSTimeZone.timeZoneForSecondsFromGMT(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class UIView
|
2
2
|
|
3
3
|
def to_s(options={})
|
4
|
-
options[:superview] = true if options[:superview].nil?
|
5
4
|
if self.respond_to? :stylename and self.stylename
|
6
5
|
suffix = ' stylename: ' + self.stylename.inspect
|
7
6
|
else
|
@@ -20,7 +19,7 @@ class UIView
|
|
20
19
|
"#{self.class.name}(##{self.object_id.to_s(16)}, #{SugarCube::Adjust::format_frame(self.frame)}" +
|
21
20
|
(inner ? ', ' + inner : '') +
|
22
21
|
')' +
|
23
|
-
(options
|
22
|
+
(options.fetch(:superview, true) && self.superview ? ", child of #{self.superview.class.name}(##{self.superview.object_id.to_s(16)})" : '') +
|
24
23
|
suffix
|
25
24
|
end
|
26
25
|
|
@@ -10,12 +10,12 @@ class UIView
|
|
10
10
|
def attr_updates(*attrs)
|
11
11
|
attr_accessor(*attrs)
|
12
12
|
attrs.each do |attr|
|
13
|
-
define_method("#{attr}=")
|
13
|
+
define_method("#{attr}=") do |value|
|
14
14
|
if instance_variable_get("@#{attr}") != value
|
15
15
|
setNeedsDisplay
|
16
16
|
end
|
17
17
|
instance_variable_set("@#{attr}", value)
|
18
|
-
|
18
|
+
end
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -80,7 +80,7 @@ class UIView
|
|
80
80
|
if use_content_size
|
81
81
|
UIGraphicsBeginImageContextWithOptions(contentSize, false, scale)
|
82
82
|
context = UIGraphicsGetCurrentContext()
|
83
|
-
subviews.each do |subview|
|
83
|
+
self.subviews.each do |subview|
|
84
84
|
CGContextSaveGState(context)
|
85
85
|
CGContextTranslateCTM(context, subview.frame.origin.x, subview.frame.origin.y)
|
86
86
|
subview.layer.renderInContext(context)
|
@@ -99,12 +99,82 @@ class UIView
|
|
99
99
|
|
100
100
|
# Returns the receiver's bounds in the coordinate system of `destination`
|
101
101
|
def convert_bounds(destination)
|
102
|
-
|
102
|
+
message = "The (ambiguously named) `convert_bounds` method has been deprecated, use `convert_frame_to` (or `convert_frame_from`)"
|
103
|
+
if defined?(SugarCube::Legacy)
|
104
|
+
SugarCube::Legacy.log(message)
|
105
|
+
else
|
106
|
+
NSLog(message)
|
107
|
+
end
|
108
|
+
return convert_frame_to(destination)
|
109
|
+
end
|
110
|
+
|
111
|
+
def convert_frame_to(destination)
|
112
|
+
return self.convertRect(CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), toView:destination)
|
113
|
+
end
|
114
|
+
|
115
|
+
def convert_frame_from(source)
|
116
|
+
return self.convertRect(CGRectMake(0, 0, source.frame.size.width, source.frame.size.height), fromView:source)
|
103
117
|
end
|
104
118
|
|
105
119
|
# Returns the receiver's bounds in the coordinate system of `destination`
|
106
120
|
def convert_origin(destination)
|
107
|
-
|
121
|
+
message = "The (ambiguously named) `convert_origin` method has been deprecated, use `convert_origin_to` (or `convert_origin_from`)"
|
122
|
+
if defined?(SugarCube::Legacy)
|
123
|
+
SugarCube::Legacy.log(message)
|
124
|
+
else
|
125
|
+
NSLog(message)
|
126
|
+
end
|
127
|
+
return self.convert_origin_to(destination)
|
128
|
+
end
|
129
|
+
|
130
|
+
def convert_origin_to(destination)
|
131
|
+
return self.convertPoint([0, 0], toView:destination)
|
132
|
+
end
|
133
|
+
|
134
|
+
def convert_origin_from(source)
|
135
|
+
return self.convertPoint([0, 0], fromView:source)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Easily get and set a UIView's frame properties
|
139
|
+
|
140
|
+
def x
|
141
|
+
self.frame.origin.x
|
142
|
+
end
|
143
|
+
|
144
|
+
def setX(newX)
|
145
|
+
newFrame = self.frame
|
146
|
+
newFrame.origin.x = newX
|
147
|
+
self.frame = newFrame
|
148
|
+
end
|
149
|
+
|
150
|
+
def y
|
151
|
+
self.frame.origin.y
|
152
|
+
end
|
153
|
+
|
154
|
+
def setY(newY)
|
155
|
+
newFrame = self.frame
|
156
|
+
newFrame.origin.y = newY
|
157
|
+
self.frame = newFrame
|
158
|
+
end
|
159
|
+
|
160
|
+
def height
|
161
|
+
self.frame.size.height
|
162
|
+
end
|
163
|
+
|
164
|
+
def setHeight(newHeight)
|
165
|
+
newFrame = self.frame
|
166
|
+
newFrame.size.height = newHeight
|
167
|
+
self.frame = newFrame
|
168
|
+
end
|
169
|
+
|
170
|
+
def width
|
171
|
+
self.frame.size.width
|
172
|
+
end
|
173
|
+
|
174
|
+
def setWidth(newWidth)
|
175
|
+
newFrame = self.frame
|
176
|
+
newFrame.size.width = newWidth
|
177
|
+
self.frame = newFrame
|
108
178
|
end
|
109
179
|
|
110
180
|
end
|
data/lib/sugarcube/version.rb
CHANGED
@@ -99,6 +99,10 @@ describe 'NSAttributeString' do
|
|
99
99
|
@subject.isEqualToAttributedString('test'.attrd.vertical_glyph_form(1)).should != true
|
100
100
|
end
|
101
101
|
|
102
|
+
it 'should have `letterpress`' do
|
103
|
+
@subject.isEqualToAttributedString('test'.attrd.letterpress).should != true
|
104
|
+
end
|
105
|
+
|
102
106
|
end
|
103
107
|
|
104
108
|
end
|
data/spec/nsstring_files_spec.rb
CHANGED
@@ -31,21 +31,21 @@ describe 'NSString' do
|
|
31
31
|
'little_square.png'.resource_exists?.should == true
|
32
32
|
'foo'.resource_exists?.should == false
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
describe "exists?" do
|
36
|
-
|
36
|
+
|
37
37
|
it "should not exists" do
|
38
38
|
"abc".exists?.should == false
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
it "should not exists" do
|
42
42
|
"abc".cache.exists?.should == false
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
it "should not exists" do
|
46
46
|
"abc".resource.exists?.should == false
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
describe "in document" do
|
50
50
|
before do
|
51
51
|
"abc".writeToFile "abc".document, atomically:true
|
@@ -53,12 +53,12 @@ describe 'NSString' do
|
|
53
53
|
after do
|
54
54
|
NSFileManager.defaultManager.removeItemAtPath "abc".document, error:nil
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
it "should be exists" do
|
58
58
|
"abc".exists?.should == true
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
describe "in cache" do
|
63
63
|
before do
|
64
64
|
"abc".writeToFile "abc".cache, atomically:true
|
@@ -66,7 +66,7 @@ describe 'NSString' do
|
|
66
66
|
after do
|
67
67
|
NSFileManager.defaultManager.removeItemAtPath "abc".cache, error:nil
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
it "should be exists" do
|
71
71
|
"abc".cache.exists?.should == true
|
72
72
|
end
|
@@ -79,9 +79,9 @@ describe 'NSString' do
|
|
79
79
|
end
|
80
80
|
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
describe "remove!" do
|
84
|
-
|
84
|
+
|
85
85
|
describe "in document" do
|
86
86
|
before do
|
87
87
|
"abc".writeToFile "abc".document, atomically:true
|
@@ -89,13 +89,13 @@ describe 'NSString' do
|
|
89
89
|
after do
|
90
90
|
NSFileManager.defaultManager.removeItemAtPath "abc".document, error:nil
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
it "should remove" do
|
94
94
|
"abc".remove!.should == nil
|
95
95
|
"abc".exists?.should == false
|
96
96
|
end
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
describe "in cache" do
|
100
100
|
before do
|
101
101
|
"abc".writeToFile "abc".cache, atomically:true
|
@@ -103,14 +103,14 @@ describe 'NSString' do
|
|
103
103
|
after do
|
104
104
|
NSFileManager.defaultManager.removeItemAtPath "abc".cache, error:nil
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
it "should remove" do
|
108
108
|
path = "abc".cache
|
109
109
|
path.remove!.should == nil
|
110
110
|
path.exists?.should == false
|
111
111
|
end
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
end
|
115
115
|
|
116
116
|
describe 'resource()' do
|
@@ -153,7 +153,10 @@ describe 'NSString' do
|
|
153
153
|
describe '"PkgInfo".resource_url' do
|
154
154
|
before { @it = "PkgInfo".resource_url.absoluteString }
|
155
155
|
it 'should start with "file://localhost/Users"' do
|
156
|
-
|
156
|
+
(
|
157
|
+
@it.hasPrefix("file://localhost/Users") ||
|
158
|
+
@it.hasPrefix("file:///Users")
|
159
|
+
).should == true
|
157
160
|
end
|
158
161
|
it 'should end with "SugarCube_spec.app/PkgInfo"' do
|
159
162
|
@it.hasSuffix("SugarCube_spec.app/PkgInfo").should == true
|
data/spec/uiactionsheet_spec.rb
CHANGED
@@ -249,13 +249,13 @@ describe 'UIActionSheet' do
|
|
249
249
|
proper_wait 0.6
|
250
250
|
end
|
251
251
|
|
252
|
-
it 'should call block with
|
252
|
+
it 'should call block with :cancel when cancel button is pressed' do
|
253
253
|
@alert.cancelButtonIndex.should == 3
|
254
254
|
@alert.dismissWithClickedButtonIndex(@alert.cancelButtonIndex, animated: false)
|
255
255
|
@touched.should == :cancel
|
256
256
|
end
|
257
257
|
|
258
|
-
it 'should call block with
|
258
|
+
it 'should call block with :destructive when destructive button is pressed' do
|
259
259
|
@alert.destructiveButtonIndex.should == 0
|
260
260
|
@alert.dismissWithClickedButtonIndex(@alert.destructiveButtonIndex, animated: false)
|
261
261
|
@touched.should == :destructive
|
@@ -276,4 +276,42 @@ describe 'UIActionSheet' do
|
|
276
276
|
|
277
277
|
end
|
278
278
|
|
279
|
+
describe 'with success handler defined' do
|
280
|
+
|
281
|
+
before do
|
282
|
+
@touched = nil
|
283
|
+
@alert = UIActionSheet.alert('test',
|
284
|
+
buttons: ['cancel', 'destructive', 'test1', 'test2'],
|
285
|
+
success: ->(button, index){ @touched, @touched_index = button, index },
|
286
|
+
)
|
287
|
+
proper_wait 0.6
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'should not call block when cancel button is pressed' do
|
291
|
+
@alert.cancelButtonIndex.should == 3
|
292
|
+
@alert.dismissWithClickedButtonIndex(@alert.cancelButtonIndex, animated: false)
|
293
|
+
@touched.should == nil
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'should not call block when destructive button is pressed' do
|
297
|
+
@alert.destructiveButtonIndex.should == 0
|
298
|
+
@alert.dismissWithClickedButtonIndex(@alert.destructiveButtonIndex, animated: false)
|
299
|
+
@touched.should == nil
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'should call block with "test1" when first button is pressed' do
|
303
|
+
@alert.firstOtherButtonIndex.should == 1
|
304
|
+
@alert.dismissWithClickedButtonIndex(@alert.firstOtherButtonIndex, animated: false)
|
305
|
+
@touched.should == 'test1'
|
306
|
+
@touched_index.should == @alert.firstOtherButtonIndex
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'should call block with "test2" when second button is pressed' do
|
310
|
+
@alert.dismissWithClickedButtonIndex(@alert.firstOtherButtonIndex + 1, animated: false)
|
311
|
+
@touched.should == 'test2'
|
312
|
+
@touched_index.should == @alert.firstOtherButtonIndex + 1
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
279
317
|
end
|
data/spec/uialertview_spec.rb
CHANGED
@@ -9,14 +9,14 @@ describe 'UIAlertView' do
|
|
9
9
|
|
10
10
|
it 'should have :show option (show: true)' do
|
11
11
|
alert = UIAlertView.alert('test', show: true)
|
12
|
-
proper_wait
|
12
|
+
proper_wait 1
|
13
13
|
alert.visible?.should == true
|
14
14
|
alert.dismissWithClickedButtonIndex(alert.firstOtherButtonIndex, animated: false)
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'should show by default' do
|
18
18
|
alert = UIAlertView.alert('test')
|
19
|
-
proper_wait
|
19
|
+
proper_wait 1
|
20
20
|
alert.visible?.should == true
|
21
21
|
alert.dismissWithClickedButtonIndex(alert.firstOtherButtonIndex, animated: false)
|
22
22
|
end
|
@@ -144,6 +144,10 @@ describe 'UIAlertView' do
|
|
144
144
|
)
|
145
145
|
end
|
146
146
|
|
147
|
+
after do
|
148
|
+
@alert.dismissWithClickedButtonIndex(@alert.cancelButtonIndex, animated: false)
|
149
|
+
end
|
150
|
+
|
147
151
|
it 'should work for :cancel' do
|
148
152
|
proper_wait 0.6
|
149
153
|
@alert.dismissWithClickedButtonIndex(@alert.cancelButtonIndex, animated: false)
|
@@ -167,13 +171,16 @@ describe 'UIAlertView' do
|
|
167
171
|
end
|
168
172
|
|
169
173
|
it 'should work with :secure_text_input' do
|
174
|
+
@called = false
|
170
175
|
alert = UIAlertView.alert('test', buttons: ['cancel', 'ok'], style: :secure_text_input) { |button, text|
|
176
|
+
@called = true
|
171
177
|
@text = text
|
172
178
|
}
|
173
179
|
proper_wait 0.6
|
174
180
|
alert.textFieldAtIndex(0).text = 'test text'
|
175
|
-
alert.dismissWithClickedButtonIndex(alert.
|
181
|
+
alert.dismissWithClickedButtonIndex(alert.firstOtherButtonIndex, animated: false)
|
176
182
|
|
183
|
+
@called.should == true
|
177
184
|
@text.should == 'test text'
|
178
185
|
end
|
179
186
|
|