rubymotionr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +1 -0
- data/lib/rubymotionr.rb +7 -0
- data/lib/rubymotionr/project.rb +18 -0
- data/lib/rubymotionr/repo.rb +18 -0
- data/lib/rubymotionr/version.rb +3 -0
- data/rubymotionr.gemspec +19 -0
- data/spec/.DS_Store +0 -0
- data/spec/multiple-rubymotion-apps/Beers/README +5 -0
- data/spec/multiple-rubymotion-apps/Beers/Rakefile +8 -0
- data/spec/multiple-rubymotion-apps/Beers/app/app_delegate.rb +16 -0
- data/spec/multiple-rubymotion-apps/Beers/app/beer.rb +22 -0
- data/spec/multiple-rubymotion-apps/Beers/app/beer_details_controller.rb +15 -0
- data/spec/multiple-rubymotion-apps/Beers/app/beer_list_controller.rb +40 -0
- data/spec/multiple-rubymotion-apps/Beers/app/beer_map_controller.rb +51 -0
- data/spec/multiple-rubymotion-apps/Beers/resources/.gitignore +0 -0
- data/spec/multiple-rubymotion-apps/Beers/resources/list.png +0 -0
- data/spec/multiple-rubymotion-apps/Beers/resources/map.png +0 -0
- data/spec/multiple-rubymotion-apps/CircleLayout/.gitignore +5 -0
- data/spec/multiple-rubymotion-apps/CircleLayout/README +5 -0
- data/spec/multiple-rubymotion-apps/CircleLayout/Rakefile +9 -0
- data/spec/multiple-rubymotion-apps/CircleLayout/app/app_delegate.rb +8 -0
- data/spec/multiple-rubymotion-apps/CircleLayout/app/cell.rb +13 -0
- data/spec/multiple-rubymotion-apps/CircleLayout/app/circle_layout.rb +41 -0
- data/spec/multiple-rubymotion-apps/CircleLayout/app/view_controller.rb +33 -0
- data/spec/multiple-rubymotion-apps/Fonts/README +7 -0
- data/spec/multiple-rubymotion-apps/Fonts/Rakefile +8 -0
- data/spec/multiple-rubymotion-apps/Fonts/app/app_delegate.rb +9 -0
- data/spec/multiple-rubymotion-apps/Fonts/app/detail_controller.rb +50 -0
- data/spec/multiple-rubymotion-apps/Fonts/app/root_controller.rb +65 -0
- data/spec/multiple-rubymotion-apps/Fonts/resources/.gitkeep +0 -0
- data/spec/multiple-rubymotion-apps/GestureTable/README +8 -0
- data/spec/multiple-rubymotion-apps/GestureTable/Rakefile +8 -0
- data/spec/multiple-rubymotion-apps/GestureTable/app/UIColor_extensions.rb +32 -0
- data/spec/multiple-rubymotion-apps/GestureTable/app/UITableView_extensions.rb +7 -0
- data/spec/multiple-rubymotion-apps/GestureTable/app/app_delegate.rb +9 -0
- data/spec/multiple-rubymotion-apps/GestureTable/app/gesture_recognizer.rb +361 -0
- data/spec/multiple-rubymotion-apps/GestureTable/app/transformable_cells.rb +92 -0
- data/spec/multiple-rubymotion-apps/GestureTable/app/view_controller.rb +173 -0
- data/spec/multiple-rubymotion-apps/GestureTable/resources/.gitignore +0 -0
- data/spec/multiple-rubymotion-apps/Hello/README +3 -0
- data/spec/multiple-rubymotion-apps/Hello/Rakefile +8 -0
- data/spec/multiple-rubymotion-apps/Hello/app/app_delegate.rb +9 -0
- data/spec/multiple-rubymotion-apps/Hello/app/hello_view.rb +33 -0
- data/spec/multiple-rubymotion-apps/Hello/app/hello_view_controller.rb +5 -0
- data/spec/multiple-rubymotion-apps/Hello/resources/.gitignore +0 -0
- data/spec/multiple-rubymotion-apps/Hello/resources/icon.png +0 -0
- data/spec/multiple-rubymotion-apps/HelloGL/README +9 -0
- data/spec/multiple-rubymotion-apps/HelloGL/Rakefile +11 -0
- data/spec/multiple-rubymotion-apps/HelloGL/app/app_delegate.rb +16 -0
- data/spec/multiple-rubymotion-apps/HelloGL/app/index_data.rb +19 -0
- data/spec/multiple-rubymotion-apps/HelloGL/app/motiongl_controller.rb +139 -0
- data/spec/multiple-rubymotion-apps/HelloGL/app/vertex_data.rb +22 -0
- data/spec/multiple-rubymotion-apps/HelloGL/resources/rubymotion.png +0 -0
- data/spec/multiple-rubymotion-apps/Locations/README +3 -0
- data/spec/multiple-rubymotion-apps/Locations/Rakefile +8 -0
- data/spec/multiple-rubymotion-apps/Locations/app/app_delegate.rb +11 -0
- data/spec/multiple-rubymotion-apps/Locations/app/location.rb +21 -0
- data/spec/multiple-rubymotion-apps/Locations/app/locations_controller.rb +66 -0
- data/spec/multiple-rubymotion-apps/Locations/app/locations_store.rb +61 -0
- data/spec/multiple-rubymotion-apps/Locations/resources/.gitignore +0 -0
- data/spec/multiple-rubymotion-apps/Mustache/README +5 -0
- data/spec/multiple-rubymotion-apps/Mustache/Rakefile +10 -0
- data/spec/multiple-rubymotion-apps/Mustache/app/app_delegate.rb +9 -0
- data/spec/multiple-rubymotion-apps/Mustache/app/mustache_view_controller.rb +99 -0
- data/spec/multiple-rubymotion-apps/Mustache/resources/.gitignore +0 -0
- data/spec/multiple-rubymotion-apps/Mustache/resources/guido.jpg +0 -0
- data/spec/multiple-rubymotion-apps/Mustache/resources/jmccolor.jpg +0 -0
- data/spec/multiple-rubymotion-apps/Mustache/resources/kay.jpg +0 -0
- data/spec/multiple-rubymotion-apps/Mustache/resources/matz.jpg +0 -0
- data/spec/multiple-rubymotion-apps/Mustache/resources/mustache.png +0 -0
- data/spec/multiple-rubymotion-apps/Paint/README +3 -0
- data/spec/multiple-rubymotion-apps/Paint/Rakefile +8 -0
- data/spec/multiple-rubymotion-apps/Paint/app/app_delegate.rb +9 -0
- data/spec/multiple-rubymotion-apps/Paint/app/paint_view.rb +57 -0
- data/spec/multiple-rubymotion-apps/Paint/app/paint_view_controller.rb +13 -0
- data/spec/multiple-rubymotion-apps/Paint/resources/.gitignore +0 -0
- data/spec/multiple-rubymotion-apps/Paint/resources/erase.caf +0 -0
- data/spec/multiple-rubymotion-apps/PaintHTML/README +8 -0
- data/spec/multiple-rubymotion-apps/PaintHTML/Rakefile +10 -0
- data/spec/multiple-rubymotion-apps/PaintHTML/app/app_delegate.rb +10 -0
- data/spec/multiple-rubymotion-apps/PaintHTML/app/web_view_controller.rb +48 -0
- data/spec/multiple-rubymotion-apps/PaintHTML/resources/index.html +211 -0
- data/spec/multiple-rubymotion-apps/PaintHTML/resources/index2.html +14 -0
- data/spec/multiple-rubymotion-apps/README.rdoc +47 -0
- data/spec/multiple-rubymotion-apps/StoryboardCustoms/README.md +42 -0
- data/spec/multiple-rubymotion-apps/StoryboardCustoms/Rakefile +7 -0
- data/spec/multiple-rubymotion-apps/StoryboardCustoms/app/app_delegate.rb +12 -0
- data/spec/multiple-rubymotion-apps/StoryboardCustoms/app/custom_view_controller1.rb +31 -0
- data/spec/multiple-rubymotion-apps/StoryboardCustoms/app/custom_view_controller2.rb +29 -0
- data/spec/multiple-rubymotion-apps/StoryboardCustoms/app/helpers.rb +4 -0
- data/spec/multiple-rubymotion-apps/StoryboardCustoms/resources/Master.storyboard +127 -0
- data/spec/multiple-rubymotion-apps/TicTacToe/.gitignore +5 -0
- data/spec/multiple-rubymotion-apps/TicTacToe/Rakefile +7 -0
- data/spec/multiple-rubymotion-apps/TicTacToe/app/app_delegate.rb +8 -0
- data/spec/multiple-rubymotion-apps/TicTacToe/app/board.rb +59 -0
- data/spec/multiple-rubymotion-apps/TicTacToe/app/board_view_controller.rb +86 -0
- data/spec/multiple-rubymotion-apps/Timer/README +3 -0
- data/spec/multiple-rubymotion-apps/Timer/Rakefile +7 -0
- data/spec/multiple-rubymotion-apps/Timer/app/app_delegate.rb +9 -0
- data/spec/multiple-rubymotion-apps/Timer/app/timer_controller.rb +38 -0
- data/spec/multiple-rubymotion-apps/Timer/resources/.gitignore +0 -0
- data/spec/multiple-rubymotion-apps/Trollify/README +6 -0
- data/spec/multiple-rubymotion-apps/Trollify/Rakefile +9 -0
- data/spec/multiple-rubymotion-apps/Trollify/app/app_delegate.rb +9 -0
- data/spec/multiple-rubymotion-apps/Trollify/app/image_composition_controller.rb +193 -0
- data/spec/multiple-rubymotion-apps/Trollify/app/uiimage_extension.rb +27 -0
- data/spec/multiple-rubymotion-apps/Trollify/resources/.gitignore +0 -0
- data/spec/multiple-rubymotion-apps/Trollify/resources/ballmer.jpg +0 -0
- data/spec/multiple-rubymotion-apps/Trollify/resources/icon.png +0 -0
- data/spec/multiple-rubymotion-apps/Trollify/resources/icon@2x.png +0 -0
- data/spec/multiple-rubymotion-apps/Trollify/resources/trollface.png +0 -0
- data/spec/multiple-rubymotion-apps/Tweets/README +3 -0
- data/spec/multiple-rubymotion-apps/Tweets/Rakefile +7 -0
- data/spec/multiple-rubymotion-apps/Tweets/app/app_delegate.rb +9 -0
- data/spec/multiple-rubymotion-apps/Tweets/app/data_parser.rb +10 -0
- data/spec/multiple-rubymotion-apps/Tweets/app/json_parser.rb +12 -0
- data/spec/multiple-rubymotion-apps/Tweets/app/tweet.rb +11 -0
- data/spec/multiple-rubymotion-apps/Tweets/app/tweet_cell.rb +51 -0
- data/spec/multiple-rubymotion-apps/Tweets/app/tweets_controller.rb +72 -0
- data/spec/multiple-rubymotion-apps/Tweets/resources/.gitignore +0 -0
- data/spec/repo_spec.rb +27 -0
- data/spec/rubymotion-example/.DS_Store +0 -0
- data/spec/rubymotion-example/README +3 -0
- data/spec/rubymotion-example/Rakefile +7 -0
- data/spec/rubymotion-example/app/app_delegate.rb +9 -0
- data/spec/rubymotion-example/app/data_parser.rb +10 -0
- data/spec/rubymotion-example/app/json_parser.rb +12 -0
- data/spec/rubymotion-example/app/tweet.rb +11 -0
- data/spec/rubymotion-example/app/tweet_cell.rb +51 -0
- data/spec/rubymotion-example/app/tweets_controller.rb +72 -0
- data/spec/rubymotion-example/resources/.gitignore +0 -0
- data/spec/spec_helper.rb +9 -0
- metadata +309 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
class VertexData
|
2
|
+
attr_accessor :ptr
|
3
|
+
|
4
|
+
def initialize(data)
|
5
|
+
@vertex_size = data[0].count
|
6
|
+
@num_vertices = data.count
|
7
|
+
@ptr = Pointer.new(:float, @vertex_size * @num_vertices)
|
8
|
+
set_data(data)
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_data(vertices)
|
12
|
+
vertices.each_with_index do |vertex,idx|
|
13
|
+
vertex.each_with_index do |component, component_idx|
|
14
|
+
@ptr[idx * @vertex_size + component_idx] = component
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def size
|
20
|
+
@vertex_size * @num_vertices
|
21
|
+
end
|
22
|
+
end
|
Binary file
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class AppDelegate
|
2
|
+
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
3
|
+
nav = UINavigationController.alloc.initWithRootViewController(LocationsController.alloc.init)
|
4
|
+
nav.wantsFullScreenLayout = true
|
5
|
+
nav.toolbarHidden = true
|
6
|
+
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
|
7
|
+
@window.rootViewController = nav
|
8
|
+
@window.makeKeyAndVisible
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Location < NSManagedObject
|
2
|
+
def self.entity
|
3
|
+
@entity ||= begin
|
4
|
+
# Create the entity for our Location class. The entity has 3 properties. CoreData will appropriately define accessor methods for the properties.
|
5
|
+
entity = NSEntityDescription.alloc.init
|
6
|
+
entity.name = 'Location'
|
7
|
+
entity.managedObjectClassName = 'Location'
|
8
|
+
entity.properties =
|
9
|
+
['creation_date', NSDateAttributeType,
|
10
|
+
'latitude', NSDoubleAttributeType,
|
11
|
+
'longitude', NSDoubleAttributeType].each_slice(2).map do |name, type|
|
12
|
+
property = NSAttributeDescription.alloc.init
|
13
|
+
property.name = name
|
14
|
+
property.attributeType = type
|
15
|
+
property.optional = false
|
16
|
+
property
|
17
|
+
end
|
18
|
+
entity
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class LocationsController < UITableViewController
|
2
|
+
def viewDidLoad
|
3
|
+
view.dataSource = view.delegate = self
|
4
|
+
end
|
5
|
+
|
6
|
+
def viewWillAppear(animated)
|
7
|
+
navigationItem.title = 'Locations'
|
8
|
+
navigationItem.leftBarButtonItem = editButtonItem
|
9
|
+
navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithBarButtonSystemItem(UIBarButtonSystemItemAdd, target:self, action:'addLocation')
|
10
|
+
|
11
|
+
# The Add button is disabled by default, and will be enabled once the location manager is ready to return the current location.
|
12
|
+
navigationItem.rightBarButtonItem.enabled = false
|
13
|
+
@location_manager ||= CLLocationManager.alloc.init.tap do |lm|
|
14
|
+
lm.desiredAccuracy = KCLLocationAccuracyNearestTenMeters
|
15
|
+
lm.startUpdatingLocation
|
16
|
+
lm.delegate = self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def addLocation
|
21
|
+
LocationsStore.shared.add_location do |location|
|
22
|
+
# We set up our new Location object here.
|
23
|
+
coordinate = @location_manager.location.coordinate
|
24
|
+
location.creation_date = NSDate.date
|
25
|
+
location.latitude = coordinate.latitude
|
26
|
+
location.longitude = coordinate.longitude
|
27
|
+
end
|
28
|
+
view.reloadData
|
29
|
+
end
|
30
|
+
|
31
|
+
def tableView(tableView, numberOfRowsInSection:section)
|
32
|
+
LocationsStore.shared.locations.size
|
33
|
+
end
|
34
|
+
|
35
|
+
CellID = 'CellIdentifier'
|
36
|
+
def tableView(tableView, cellForRowAtIndexPath:indexPath)
|
37
|
+
cell = tableView.dequeueReusableCellWithIdentifier(CellID) || UITableViewCell.alloc.initWithStyle(UITableViewCellStyleSubtitle, reuseIdentifier:CellID)
|
38
|
+
location = LocationsStore.shared.locations[indexPath.row]
|
39
|
+
|
40
|
+
@date_formatter ||= NSDateFormatter.alloc.init.tap do |df|
|
41
|
+
df.timeStyle = NSDateFormatterMediumStyle
|
42
|
+
df.dateStyle = NSDateFormatterMediumStyle
|
43
|
+
end
|
44
|
+
cell.textLabel.text = @date_formatter.stringFromDate(location.creation_date)
|
45
|
+
cell.detailTextLabel.text = "%0.3f, %0.3f" % [location.latitude, location.longitude]
|
46
|
+
cell
|
47
|
+
end
|
48
|
+
|
49
|
+
def tableView(tableView, editingStyleForRowAtIndexPath:indexPath)
|
50
|
+
UITableViewCellEditingStyleDelete
|
51
|
+
end
|
52
|
+
|
53
|
+
def tableView(tableView, commitEditingStyle:editingStyle, forRowAtIndexPath:indexPath)
|
54
|
+
location = LocationsStore.shared.locations[indexPath.row]
|
55
|
+
LocationsStore.shared.remove_location(location)
|
56
|
+
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation:UITableViewRowAnimationFade)
|
57
|
+
end
|
58
|
+
|
59
|
+
def locationManager(manager, didUpdateToLocation:newLocation, fromLocation:oldLocation)
|
60
|
+
navigationItem.rightBarButtonItem.enabled = true
|
61
|
+
end
|
62
|
+
|
63
|
+
def locationManager(manager, didFailWithError:error)
|
64
|
+
navigationItem.rightBarButtonItem.enabled = false
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class LocationsStore
|
2
|
+
def self.shared
|
3
|
+
# Our store is a singleton object.
|
4
|
+
@shared ||= LocationsStore.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def locations
|
8
|
+
@locations ||= begin
|
9
|
+
# Fetch all locations from the model, sorting by the creation date.
|
10
|
+
request = NSFetchRequest.alloc.init
|
11
|
+
request.entity = NSEntityDescription.entityForName('Location', inManagedObjectContext:@context)
|
12
|
+
request.sortDescriptors = [NSSortDescriptor.alloc.initWithKey('creation_date', ascending:false)]
|
13
|
+
|
14
|
+
error_ptr = Pointer.new(:object)
|
15
|
+
data = @context.executeFetchRequest(request, error:error_ptr)
|
16
|
+
if data == nil
|
17
|
+
raise "Error when fetching data: #{error_ptr[0].description}"
|
18
|
+
end
|
19
|
+
data
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_location
|
24
|
+
# Yield a blank, newly created Location entity, then save the model.
|
25
|
+
yield NSEntityDescription.insertNewObjectForEntityForName('Location', inManagedObjectContext:@context)
|
26
|
+
save
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_location(location)
|
30
|
+
# Delete the given entity, then save the model.
|
31
|
+
@context.deleteObject(location)
|
32
|
+
save
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
# Create the model programmatically. Our model has only one entity, the Location class, and the data will be stored in a SQLite database, inside the application's Documents folder.
|
39
|
+
model = NSManagedObjectModel.alloc.init
|
40
|
+
model.entities = [Location.entity]
|
41
|
+
|
42
|
+
store = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(model)
|
43
|
+
store_url = NSURL.fileURLWithPath(File.join(NSHomeDirectory(), 'Documents', 'Locations.sqlite'))
|
44
|
+
error_ptr = Pointer.new(:object)
|
45
|
+
unless store.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:store_url, options:nil, error:error_ptr)
|
46
|
+
raise "Can't add persistent SQLite store: #{error_ptr[0].description}"
|
47
|
+
end
|
48
|
+
|
49
|
+
context = NSManagedObjectContext.alloc.init
|
50
|
+
context.persistentStoreCoordinator = store
|
51
|
+
@context = context
|
52
|
+
end
|
53
|
+
|
54
|
+
def save
|
55
|
+
error_ptr = Pointer.new(:object)
|
56
|
+
unless @context.save(error_ptr)
|
57
|
+
raise "Error when saving the model: #{error_ptr[0].description}"
|
58
|
+
end
|
59
|
+
@locations = nil
|
60
|
+
end
|
61
|
+
end
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
$:.unshift("/Library/RubyMotion/lib")
|
2
|
+
require 'motion/project'
|
3
|
+
|
4
|
+
#ENV['debug'] ||= '0' # Because the REPL cannot be loaded with CoreImage yet.
|
5
|
+
|
6
|
+
Motion::Project::App.setup do |app|
|
7
|
+
# Use `rake config' to see complete project settings.
|
8
|
+
app.name = 'Mustache'
|
9
|
+
app.frameworks += ['QuartzCore', 'CoreImage']
|
10
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class AppDelegate
|
2
|
+
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
3
|
+
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
|
4
|
+
@window.rootViewController = MustacheViewController.alloc.init
|
5
|
+
@window.rootViewController.wantsFullScreenLayout = true
|
6
|
+
@window.makeKeyAndVisible
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
class MustacheViewController < UIViewController
|
2
|
+
def loadView
|
3
|
+
self.view = UIImageView.alloc.init
|
4
|
+
@debug_face = false # Set to true to debug face features.
|
5
|
+
end
|
6
|
+
|
7
|
+
def viewDidLoad
|
8
|
+
@images = %w{matz guido kay jmccolor}.map { |name| UIImage.imageNamed(name + '.jpg') }
|
9
|
+
view.image = @images.first
|
10
|
+
view.contentMode = UIViewContentModeScaleAspectFit
|
11
|
+
view.userInteractionEnabled = true
|
12
|
+
|
13
|
+
previousGesture = UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'swipePreviousGesture:')
|
14
|
+
previousGesture.direction = UISwipeGestureRecognizerDirectionLeft
|
15
|
+
view.addGestureRecognizer(previousGesture)
|
16
|
+
nextGesture = UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'swipeNextGesture:')
|
17
|
+
nextGesture.direction = UISwipeGestureRecognizerDirectionRight
|
18
|
+
view.addGestureRecognizer(nextGesture)
|
19
|
+
end
|
20
|
+
|
21
|
+
def viewDidAppear(animated)
|
22
|
+
mustachify
|
23
|
+
end
|
24
|
+
|
25
|
+
def mustachify
|
26
|
+
# Remove previous mustaches.
|
27
|
+
view.subviews.each { |v| v.removeFromSuperview }
|
28
|
+
|
29
|
+
# CoreImage used a coordinate system which is flipped on the Y axis
|
30
|
+
# compared to UIKit. Also, a UIImageView can return an image larger than
|
31
|
+
# itself. To properly translate points, we use an affine transform.
|
32
|
+
transform = CGAffineTransformMakeScale(view.bounds.size.width / view.image.size.width, -1 * (view.bounds.size.height / view.image.size.height))
|
33
|
+
transform = CGAffineTransformTranslate(transform, 0, -view.image.size.height)
|
34
|
+
|
35
|
+
image = CIImage.imageWithCGImage(view.image.CGImage)
|
36
|
+
@detector ||= CIDetector.detectorOfType CIDetectorTypeFace, context:nil, options: { CIDetectorAccuracy: CIDetectorAccuracyHigh }
|
37
|
+
@detector.featuresInImage(image).each do |feature|
|
38
|
+
# We need the mouth and eyes positions to determine where the mustache
|
39
|
+
# should be added.
|
40
|
+
next unless feature.hasMouthPosition and feature.hasLeftEyePosition and feature.hasRightEyePosition
|
41
|
+
|
42
|
+
if @debug_face
|
43
|
+
[feature.leftEyePosition,feature.rightEyePosition,feature.mouthPosition].each do |pt|
|
44
|
+
v = UIView.alloc.initWithFrame CGRectMake(0, 0, 20, 20)
|
45
|
+
v.backgroundColor = UIColor.greenColor.colorWithAlphaComponent(0.2)
|
46
|
+
pt = CGPointApplyAffineTransform(pt, transform)
|
47
|
+
v.center = pt
|
48
|
+
view.addSubview(v)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create the mustache view.
|
53
|
+
mustacheView = UIImageView.alloc.init
|
54
|
+
mustacheView.image = UIImage.imageNamed('mustache')
|
55
|
+
mustacheView.contentMode = UIViewContentModeScaleAspectFit
|
56
|
+
|
57
|
+
# Compute its location and size, based on the position of the eyes and
|
58
|
+
# mouth.
|
59
|
+
w = feature.bounds.size.width
|
60
|
+
h = feature.bounds.size.height / 5
|
61
|
+
x = (feature.mouthPosition.x + (feature.leftEyePosition.x + feature.rightEyePosition.x) / 2) / 2 - w / 2
|
62
|
+
y = feature.mouthPosition.y
|
63
|
+
mustacheView.frame = CGRectApplyAffineTransform([[x, y], [w, h]], transform)
|
64
|
+
|
65
|
+
# Apply a rotation on the mustache, based on the face inclination.
|
66
|
+
mustacheAngle = Math.atan2(feature.leftEyePosition.x - feature.rightEyePosition.x, feature.leftEyePosition.y - feature.rightEyePosition.y) + Math::PI/2
|
67
|
+
mustacheView.transform = CGAffineTransformMakeRotation(mustacheAngle)
|
68
|
+
|
69
|
+
view.addSubview(mustacheView)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def shouldAutorotateToInterfaceOrientation(*)
|
74
|
+
mustachify
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def swipePreviousGesture(gesture)
|
79
|
+
idx = @images.index(view.image)
|
80
|
+
view.image =
|
81
|
+
if idx == 0
|
82
|
+
@images.last
|
83
|
+
else
|
84
|
+
@images[idx - 1]
|
85
|
+
end
|
86
|
+
mustachify
|
87
|
+
end
|
88
|
+
|
89
|
+
def swipeNextGesture(gesture)
|
90
|
+
idx = @images.index(view.image)
|
91
|
+
view.image =
|
92
|
+
if idx == @images.size - 1
|
93
|
+
@images.first
|
94
|
+
else
|
95
|
+
@images[idx + 1]
|
96
|
+
end
|
97
|
+
mustachify
|
98
|
+
end
|
99
|
+
end
|
File without changes
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class AppDelegate
|
2
|
+
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
3
|
+
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
|
4
|
+
@window.rootViewController = PaintViewController.alloc.init
|
5
|
+
@window.rootViewController.wantsFullScreenLayout = true
|
6
|
+
@window.makeKeyAndVisible
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class PaintView < UIView
|
2
|
+
def initWithFrame(ect)
|
3
|
+
if super
|
4
|
+
path = NSBundle.mainBundle.pathForResource('erase', ofType:'caf')
|
5
|
+
url = NSURL.fileURLWithPath(path)
|
6
|
+
error_ptr = Pointer.new(:id)
|
7
|
+
@eraseSound = AVAudioPlayer.alloc.initWithContentsOfURL(url,
|
8
|
+
error:error_ptr)
|
9
|
+
unless @eraseSound
|
10
|
+
raise "Can't open sound file: #{error_ptr[0].description}"
|
11
|
+
end
|
12
|
+
@paths = []
|
13
|
+
end
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def drawRect(rect)
|
18
|
+
UIColor.blackColor.set
|
19
|
+
UIBezierPath.bezierPathWithRect(rect).fill
|
20
|
+
@paths.each do |path, color|
|
21
|
+
color.set
|
22
|
+
path.stroke
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def touchesBegan(touches, withEvent:event)
|
27
|
+
bp = UIBezierPath.alloc.init
|
28
|
+
bp.lineWidth = 3.0
|
29
|
+
random_color = begin
|
30
|
+
red, green, blue = rand(100)/100.0, rand(100)/100.0, rand(100)/100.0
|
31
|
+
UIColor.alloc.initWithRed(red/100.0, green:green, blue:blue, alpha:1.0)
|
32
|
+
end
|
33
|
+
@paths << [bp, random_color]
|
34
|
+
end
|
35
|
+
|
36
|
+
def touchesMoved(touches, withEvent:event)
|
37
|
+
touch = event.touchesForView(self).anyObject
|
38
|
+
point = touch.locationInView(self)
|
39
|
+
if @previousPoint and !@paths.empty?
|
40
|
+
bp = @paths.last.first
|
41
|
+
bp.moveToPoint(@previousPoint)
|
42
|
+
bp.addLineToPoint(point)
|
43
|
+
end
|
44
|
+
@previousPoint = point
|
45
|
+
setNeedsDisplay
|
46
|
+
end
|
47
|
+
|
48
|
+
def touchesEnded(touches, withEvent:event)
|
49
|
+
@previousPoint = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def eraseContent
|
53
|
+
@paths.clear
|
54
|
+
@eraseSound.play
|
55
|
+
setNeedsDisplay
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class PaintViewController < UIViewController
|
2
|
+
def loadView
|
3
|
+
self.view = PaintView.alloc.init
|
4
|
+
end
|
5
|
+
|
6
|
+
def canBecomeFirstResponder
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def motionEnded(motion, withEvent:event)
|
11
|
+
self.view.eraseContent if motion == UIEventSubtypeMotionShake
|
12
|
+
end
|
13
|
+
end
|