Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial revision

  • Loading branch information...
commit 03fc307001503ae7fae7996e26f007d25ad2f23e 0 parents
Laurent Sansonetti authored
Showing with 1,673 additions and 0 deletions.
  1. +3 −0  .gitignore
  2. +1 −0  Beers/LEGAL
  3. +8 −0 Beers/Rakefile
  4. +16 −0 Beers/app/app_delegate.rb
  5. +22 −0 Beers/app/beer.rb
  6. +15 −0 Beers/app/beer_details_controller.rb
  7. +40 −0 Beers/app/beer_list_controller.rb
  8. +51 −0 Beers/app/beer_map_controller.rb
  9. BIN  Beers/resources/list.png
  10. BIN  Beers/resources/map.png
  11. +4 −0 GestureTable/README
  12. +8 −0 GestureTable/Rakefile
  13. +32 −0 GestureTable/app/UIColor_extensions.rb
  14. +7 −0 GestureTable/app/UITableView_extensions.rb
  15. +9 −0 GestureTable/app/app_delegate.rb
  16. +361 −0 GestureTable/app/gesture_recognizer.rb
  17. +92 −0 GestureTable/app/transformable_cells.rb
  18. +173 −0 GestureTable/app/view_controller.rb
  19. +8 −0 Hello/Rakefile
  20. +9 −0 Hello/app/app_delegate.rb
  21. +33 −0 Hello/app/hello_view.rb
  22. +5 −0 Hello/app/hello_view_controller.rb
  23. BIN  Hello/resources/icon.png
  24. +8 −0 Locations/Rakefile
  25. +11 −0 Locations/app/app_delegate.rb
  26. +21 −0 Locations/app/location.rb
  27. +66 −0 Locations/app/locations_controller.rb
  28. +61 −0 Locations/app/locations_store.rb
  29. +10 −0 Mustache/Rakefile
  30. +9 −0 Mustache/app/app_delegate.rb
  31. +99 −0 Mustache/app/mustache_view_controller.rb
  32. BIN  Mustache/resources/guido.jpg
  33. BIN  Mustache/resources/jmccolor.jpg
  34. BIN  Mustache/resources/kay.jpg
  35. BIN  Mustache/resources/matz.jpg
  36. BIN  Mustache/resources/mustache.png
  37. +8 −0 Paint/Rakefile
  38. +9 −0 Paint/app/app_delegate.rb
  39. +57 −0 Paint/app/paint_view.rb
  40. +13 −0 Paint/app/paint_view_controller.rb
  41. BIN  Paint/resources/erase.caf
  42. +2 −0  Trollify/README
  43. +9 −0 Trollify/Rakefile
  44. +9 −0 Trollify/app/app_delegate.rb
  45. +193 −0 Trollify/app/image_composition_controller.rb
  46. +27 −0 Trollify/app/uiimage_extension.rb
  47. BIN  Trollify/resources/ballmer.jpg
  48. BIN  Trollify/resources/icon.png
  49. BIN  Trollify/resources/icon@2x.png
  50. BIN  Trollify/resources/trollface.png
  51. +9 −0 Trollify/spec/main_spec.rb
  52. +7 −0 Tweets/Rakefile
  53. +9 −0 Tweets/app/app_delegate.rb
  54. +11 −0 Tweets/app/tweet.rb
  55. +51 −0 Tweets/app/tweet_cell.rb
  56. +77 −0 Tweets/app/tweets_controller.rb
3  .gitignore
@@ -0,0 +1,3 @@
+*.sw[po]
+.repl_history
+build
1  Beers/LEGAL
@@ -0,0 +1 @@
+Icon files in `resources' are from Glyphish. http://glyphish.com.
8 Beers/Rakefile
@@ -0,0 +1,8 @@
+$:.unshift('../../lib')
+require 'motion/project'
+
+Motion::Project::App.setup do |app|
+ # Use `rake config' to see complete project settings.
+ app.name = 'Beers'
+ app.frameworks += ['CoreLocation', 'MapKit', 'AddressBook']
+end
16 Beers/app/app_delegate.rb
@@ -0,0 +1,16 @@
+class AppDelegate
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
+ tabbar = UITabBarController.alloc.init
+ tabbar.viewControllers = [BeerMapController.alloc.init, BeerListController.alloc.init]
+ tabbar.selectedIndex = 0
+ @window.rootViewController = UINavigationController.alloc.initWithRootViewController(tabbar)
+ @window.rootViewController.wantsFullScreenLayout = true
+ @window.makeKeyAndVisible
+ return true
+ end
+
+ def beer_details_controller
+ @beer_details_controller ||= BeerDetailsController.alloc.init
+ end
+end
22 Beers/app/beer.rb
@@ -0,0 +1,22 @@
+class Beer
+ def initialize(lat, long, name, link)
+ @name = name
+ @coordinate = CLLocationCoordinate2D.new
+ @coordinate.latitude = lat
+ @coordinate.longitude = long
+ @url = NSURL.alloc.initWithString(link)
+ end
+
+ def title; @name; end
+ def coordinate; @coordinate; end
+ def url; @url; end
+
+ All = [
+ Beer.new(50.016667, 4.316667, 'Chimay', 'http://en.wikipedia.org/wiki/Chimay_Brewery'),
+ Beer.new(49.639722, 5.348889, 'Orval', 'http://en.wikipedia.org/wiki/Orval_Brewery'),
+ Beer.new(50.178162, 5.219879, 'Rochefort', 'http://en.wikipedia.org/wiki/Rochefort_Brewery'),
+ Beer.new(51.284720, 4.656670, 'Westmalle', 'http://en.wikipedia.org/wiki/Westmalle_Brewery'),
+ Beer.new(50.895942, 2.721262, 'Westvleteren', 'http://en.wikipedia.org/wiki/Westvleteren_Brewery'),
+ Beer.new(51.298800, 5.488570, 'Achel', 'http://en.wikipedia.org/wiki/Achel_Brewery')
+ ]
+end
15 Beers/app/beer_details_controller.rb
@@ -0,0 +1,15 @@
+class BeerDetailsController < UIViewController
+ def loadView
+ self.view = UIWebView.alloc.init
+ end
+
+ def viewWillAppear(animated)
+ navigationController.setNavigationBarHidden(false, animated:true)
+ end
+
+ def showDetailsForBeer(beer)
+ navigationItem.title = beer.title
+ request = NSURLRequest.requestWithURL(beer.url)
+ view.loadRequest(request)
+ end
+end
40 Beers/app/beer_list_controller.rb
@@ -0,0 +1,40 @@
+class BeerListController < UITableViewController
+ def init
+ if super
+ self.tabBarItem = UITabBarItem.alloc.initWithTitle('List', image:UIImage.imageNamed('list.png'), tag:1)
+ end
+ self
+ end
+
+ def viewDidLoad
+ view.dataSource = view.delegate = self
+ end
+
+ def viewWillAppear(animated)
+ navigationController.setNavigationBarHidden(true, animated:true)
+ end
+
+ def tableView(tableView, numberOfRowsInSection:section)
+ Beer::All.size
+ end
+
+ CELLID = 'CellIdentifier'
+ def tableView(tableView, cellForRowAtIndexPath:indexPath)
+ cell = tableView.dequeueReusableCellWithIdentifier(CELLID) || begin
+ cell = UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:CELLID)
+ cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton
+ cell
+ end
+
+ beer = Beer::All[indexPath.row]
+ cell.textLabel.text = beer.title
+ return cell
+ end
+
+ def tableView(tableView, accessoryButtonTappedForRowWithIndexPath:indexPath)
+ beer = Beer::All[indexPath.row]
+ controller = UIApplication.sharedApplication.delegate.beer_details_controller
+ navigationController.pushViewController(controller, animated:true)
+ controller.showDetailsForBeer(beer)
+ end
+end
51 Beers/app/beer_map_controller.rb
@@ -0,0 +1,51 @@
+class BeerMapController < UIViewController
+ def init
+ if super
+ self.tabBarItem = UITabBarItem.alloc.initWithTitle('Map', image:UIImage.imageNamed('map.png'), tag:1)
+ end
+ self
+ end
+
+ def loadView
+ self.view = MKMapView.alloc.init
+ view.delegate = self
+ end
+
+ def viewDidLoad
+ view.frame = tabBarController.view.bounds
+
+ # Center on Brussels.
+ region = MKCoordinateRegionMake(CLLocationCoordinate2D.new(50.85, 4.35), MKCoordinateSpanMake(3.1, 3.1))
+ self.view.setRegion(region)
+
+ Beer::All.each { |beer| self.view.addAnnotation(beer) }
+ end
+
+ def viewWillAppear(animated)
+ navigationController.setNavigationBarHidden(true, animated:true)
+ end
+
+ ViewIdentifier = 'ViewIdentifier'
+ def mapView(mapView, viewForAnnotation:beer)
+ if view = mapView.dequeueReusableAnnotationViewWithIdentifier(ViewIdentifier)
+ view.annotation = beer
+ else
+ view = MKPinAnnotationView.alloc.initWithAnnotation(beer, reuseIdentifier:ViewIdentifier)
+ view.canShowCallout = true
+ view.animatesDrop = true
+ button = UIButton.buttonWithType(UIButtonTypeDetailDisclosure)
+ button.addTarget(self, action: :'showDetails:', forControlEvents:UIControlEventTouchUpInside)
+ view.rightCalloutAccessoryView = button
+ end
+ return view
+ end
+
+ def showDetails(sender)
+ if view.selectedAnnotations.size == 1
+ beer = view.selectedAnnotations[0]
+ controller = UIApplication.sharedApplication.delegate.beer_details_controller
+ navigationController.pushViewController(controller, animated:true)
+ controller.showDetailsForBeer(beer)
+ end
+ end
+end
BIN  Beers/resources/list.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Beers/resources/map.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 GestureTable/README
@@ -0,0 +1,4 @@
+The code of this sample was originally a Ruby translation of the following
+Objective-C project: https://github.com/mystcolor/JTGestureBasedTableViewDemo
+
+Initial port was done by Josh Ballanco <jballanc@gmail.com>.
8 GestureTable/Rakefile
@@ -0,0 +1,8 @@
+$:.unshift('../../lib')
+require 'motion/project'
+
+Motion::Project::App.setup do |app|
+ # Use `rake config' to see complete project settings.
+ app.name = 'Gesture Table'
+ app.frameworks += ['QuartzCore']
+end
32 GestureTable/app/UIColor_extensions.rb
@@ -0,0 +1,32 @@
+class UIColor
+ def colorWithBrightness(brightnessComponent)
+ hue = Pointer.new(:float)
+ saturation = Pointer.new(:float)
+ brightness = Pointer.new(:float)
+ red = Pointer.new(:float)
+ green = Pointer.new(:float)
+ blue = Pointer.new(:float)
+ white = Pointer.new(:float)
+ alpha = Pointer.new(:float)
+
+ if getHue(hue, saturation: saturation, brightness: brightness, alpha: alpha)
+ UIColor.colorWithHue(hue[0], saturation: saturation[0], brightness: brightness[0] * brightnessComponent, alpha: alpha[0])
+ elsif getRed(red, green: green, blue: blue, alpha: alpha)
+ UIColor.colorWithRed(red[0] * brightnessComponent, green: green[0] * brightnessComponent, blue: blue[0] * brightnessComponent, alpha: alpha[0])
+ elsif getWhite(white, alpha: alpha)
+ UIColor.colorWithWhite(white[0] * brightnessComponent, alpha: alpha[0])
+ end
+ end
+
+ def colorWithHueOffset(hueOffset)
+ hue = Pointer.new(:float)
+ saturation = Pointer.new(:float)
+ brightness = Pointer.new(:float)
+ alpha = Pointer.new(:float)
+
+ if getHue(hue, saturation: saturation, brightness: brightness, alpha: alpha)
+ newHue = (hue[0] + hueOffset) % 1
+ UIColor.colorWithHue(newHue, saturation: saturation[0], brightness: brightness[0], alpha: alpha[0])
+ end
+ end
+end
7 GestureTable/app/UITableView_extensions.rb
@@ -0,0 +1,7 @@
+class UITableView
+ def reloadVisibleRowsExceptIndexPath(indexPath)
+ visibleRows = indexPathsForVisibleRows.mutableCopy
+ visibleRows.removeObject(indexPath)
+ reloadRowsAtIndexPaths(visibleRows, withRowAnimation: UITableViewRowAnimationNone)
+ end
+end
9 GestureTable/app/app_delegate.rb
@@ -0,0 +1,9 @@
+class AppDelegate
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
+ application.setStatusBarStyle(UIStatusBarStyleBlackOpaque)
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
+ @window.rootViewController = UINavigationController.alloc.initWithRootViewController(ViewController.new)
+ @window.makeKeyAndVisible
+ true
+ end
+end
361 GestureTable/app/gesture_recognizer.rb
@@ -0,0 +1,361 @@
+class GestureRecognizer
+ CommitEditingRowDefaultRowHeight = 80
+ RowAnimationDuration = 0.25
+ CellSnapshotTag = 100000
+
+ def initWithTableView(tableView, delegate:delegate)
+ if init
+ @tableView = tableView
+ @delegate= delegate
+ @state = :none
+ @tableViewDelegate = tableView.delegate
+ tableView.delegate = self
+
+ @pinchRecognizer = UIPinchGestureRecognizer.alloc.initWithTarget(self, action: :"pinchGestureRecognizer:")
+ @panRecognizer = UIPanGestureRecognizer.alloc.initWithTarget(self, action: :"panGestureRecognizer:")
+ @longPressRecognizer = UILongPressGestureRecognizer.alloc.initWithTarget(self, action: :"longPressGestureRecognizer:")
+ tableView.gestureRecognizers += [@pinchRecognizer, @panRecognizer, @longPressRecognizer]
+ @pinchRecognizer.delegate = @panRecognizer.delegate = @longPressRecognizer.delegate = self
+ @addingRowHeight = 0
+ end
+ self
+ end
+
+ def scrollTable
+ location = @longPressRecognizer.locationInView(@tableView)
+
+ currentOffset = @tableView.contentOffset
+ @scrollingRate ||= 0
+ newOffset = CGPointMake(currentOffset.x, currentOffset.y + @scrollingRate)
+ if newOffset.y < 0
+ newOffset.y = 0
+ elsif @tableView.contentSize.height < @tableView.frame.size.height
+ newOffset = currentOffset
+ elsif newOffset.y > @tableView.contentSize.height - @tableView.frame.size.height
+ newOffset.y = @tableView.contentSize.height - @tableView.frame.size.height
+ end
+ @tableView.setContentOffset(newOffset)
+
+ if location.y >= 0
+ cellSnapshotView = @tableView.viewWithTag(CellSnapshotTag)
+ cellSnapshotView.center = CGPointMake(@tableView.center.x, location.y)
+ end
+
+ updateAddingIndexPathForCurrentLocation
+ end
+
+ def updateAddingIndexPathForCurrentLocation
+ indexPath = indexPathFromRecognizer(@longPressRecognizer)
+ if indexPath != @addingIndexPath
+ @tableView.beginUpdates
+ @tableView.deleteRowsAtIndexPaths([@addingIndexPath], withRowAnimation: UITableViewRowAnimationNone)
+ @tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimationNone)
+ @delegate.gestureRecognizer(self, needsMoveRowAtIndexPath: @addingIndexPath, toIndexPath: indexPath)
+ @tableView.endUpdates
+ @addingIndexPath = indexPath
+ end
+ end
+
+ def commitOrDiscardCell
+ @tableView.beginUpdates
+
+ cell = @tableView.cellForRowAtIndexPath(@addingIndexPath)
+ commitingCellHeight =
+ if @delegate.respond_to? :"gestureRecognizer:heightForCommittingRowAtIndexPath:"
+ @delegate.gestureRecognizer(self, heightForCommittingRowAtIndexPath: @addingIndexPath)
+ else
+ @tableView.rowHeight
+ end
+ if cell.frame.size.height >= commitingCellHeight
+ @delegate.gestureRecognizer(self, needsCommitRowAtIndexPath: @addingIndexPath)
+ else
+ @delegate.gestureRecognizer(self, needsDiscardRowAtIndexPath: @addingIndexPath)
+ @tableView.deleteRowsAtIndexPaths([@addingIndexPath], withRowAnimation: UITableViewRowAnimationMiddle)
+ end
+
+ @tableView.performSelector(:"reloadVisibleRowsExceptIndexPath:", withObject: @addingIndexPath, afterDelay: RowAnimationDuration)
+ @addingIndexPath = nil
+
+ @tableView.endUpdates
+
+ UIView.beginAnimations('', context: nil)
+ UIView.setAnimationBeginsFromCurrentState(true)
+ UIView.setAnimationDuration(0.5)
+ @tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
+ UIView.commitAnimations
+
+ @state = :none
+ end
+
+ def pinchGestureRecognizer(recognizer)
+ if recognizer.state == UIGestureRecognizerStateEnded || recognizer.numberOfTouches < 2
+ self.commitOrDiscardCell if @addingIndexPath
+ return
+ end
+
+ location1 = recognizer.locationOfTouch(0, inView: @tableView)
+ location2 = recognizer.locationOfTouch(1, inView: @tableView)
+ upperPoint = location1.y < location2.y ? location1 : location2
+
+ rect = CGRectMake(*location1, location2.x - location1.x, location2.y - location1.y)
+
+ if recognizer.state == UIGestureRecognizerStateBegan
+ @state = :pinching
+ indexPaths = @tableView.indexPathsForRowsInRect(rect)
+ unless indexPaths.empty?
+ midIndex = ((indexPaths.first.row + indexPaths.last.row) / 2.0) + 0.5
+ midIndexPath = NSIndexPath.indexPathForRow(midIndex, inSection: indexPaths.first.section)
+
+ @startPinchingUpperPoint = upperPoint
+
+ if @delegate.respond_to? :"gestureRecognizer:willCreateCellAtIndexPath:"
+ @addingIndexPath = @delegate.gestureRecognizer(self, willCreateCellAtIndexPath: midIndexPath)
+ else
+ @addingIndexPath = midIndexPath
+ end
+
+ @tableView.contentInset = UIEdgeInsetsMake(@tableView.frame.size.height, 0, @tableView.frame.size.height, 0)
+
+ if @addingIndexPath
+ @tableView.beginUpdates
+ @delegate.gestureRecognizer(self, needsAddRowAtIndexPath: @addingIndexPath)
+ @tableView.insertRowsAtIndexPaths([@addingIndexPath], withRowAnimation: UITableViewRowAnimationMiddle)
+ @tableView.endUpdates
+ end
+ end
+ elsif recognizer.state == UIGestureRecognizerStateChanged
+ diffRowHeight = CGRectGetHeight(rect) - CGRectGetHeight(rect)/recognizer.scale
+ if @addingRowHeight - diffRowHeight >= 1 || @addingRowHeight - diffRowHeight <= -1
+ @addingRowHeight = diffRowHeight
+ @tableView.reloadData
+ end
+
+ newUpperPoint = upperPoint
+ diffOffsetY = @startPinchingUpperPoint.y - newUpperPoint.y
+ newOffset = CGPointMake(@tableView.contentOffset.x, @tableView.contentOffset.y + diffOffsetY)
+ @tableView.setContentOffset(newOffset, animated: false)
+ end
+ end
+
+ def panGestureRecognizer(recognizer)
+ if (recognizer.state == UIGestureRecognizerStateBegan ||
+ recognizer.state == UIGestureRecognizerStateChanged) &&
+ recognizer.numberOfTouches > 0
+
+ location1 = recognizer.locationOfTouch(0, inView: @tableView)
+ indexPath = @addingIndexPath
+ unless indexPath
+ indexPath = @tableView.indexPathForRowAtPoint(location1)
+ @addingIndexPath = indexPath
+ end
+
+ @state = :panning
+ cell = @tableView.cellForRowAtIndexPath(indexPath)
+ translation = recognizer.translationInView(@tableView)
+ cell.contentView.frame = CGRectOffset(cell.contentView.bounds, translation.x, 0)
+
+ if @delegate.respond_to? :"gestureRecognizer:didChangeContentViewTranslation:forRowAtIndexPath:"
+ @delegate.gestureRecognizer(self, didChangeContentViewTranslation: translation, forRowAtIndexPath: indexPath)
+ end
+
+ commitEditingLength = lengthForCommitEditingRowAtIndexPath(indexPath)
+ if translation.x.abs >= commitEditingLength
+ if @addingCellState == :middle
+ @addingCellState = translation.x > 0 ? :right : :left
+ end
+ else
+ if @addingCellState != :middle
+ @addingCellState = :middle
+ end
+ end
+
+ if @delegate.respond_to? :"gestureRecognizer:didEnterEditingState:forRowAtIndexPath:"
+ @delegate.gestureRecognizer(self, didEnterEditingState: @addingCellState, forRowAtIndexPath: indexPath)
+ end
+ elsif recognizer.state == UIGestureRecognizerStateEnded
+ cell = @tableView.cellForRowAtIndexPath(@addingIndexPath)
+ translation = recognizer.translationInView(@tableView)
+
+ commitEditingLength = lengthForCommitEditingRowAtIndexPath(@addingIndexPath)
+ if translation.x.abs >= commitEditingLength
+ if @delegate.respond_to? :"gestureRecognizer:commitEditingState:forRowAtIndexPath:"
+ @delegate.gestureRecognizer(self, commitEditingState: @addingCellState, forRowAtIndexPath: @addingIndexPath)
+ end
+ else
+ UIView.beginAnimations('', context: nil)
+ cell.contentView.frame = cell.contentView.bounds
+ UIView.commitAnimations
+ end
+
+ @addingCellState = :middle
+ @addingIndexPath = nil
+ @state = :none
+ end
+ end
+
+ def longPressGestureRecognizer(recognizer)
+ location = recognizer.locationInView(@tableView)
+ indexPath = indexPathFromRecognizer(recognizer)
+
+ case recognizer.state
+ when UIGestureRecognizerStateBegan
+ @state = :moving
+
+ cell = @tableView.cellForRowAtIndexPath(indexPath)
+ UIGraphicsBeginImageContextWithOptions(cell.bounds.size, false, 0)
+ cell.layer.renderInContext(UIGraphicsGetCurrentContext())
+ cellImage = UIGraphicsGetImageFromCurrentImageContext()
+ UIGraphicsEndImageContext()
+
+ snapShotView = @tableView.viewWithTag(CellSnapshotTag)
+ unless snapShotView
+ snapShotView = UIImageView.alloc.initWithImage(cellImage)
+ snapShotView.tag = CellSnapshotTag
+ @tableView.addSubview(snapShotView)
+ rect = @tableView.rectForRowAtIndexPath(indexPath)
+ snapShotView.frame = CGRectOffset(snapShotView.bounds, rect.origin.x, rect.origin.y)
+ end
+
+ UIView.beginAnimations('zoomCell', context: nil)
+ snapShotView.transform = CGAffineTransformMakeScale(1.1, 1.1)
+ snapShotView.center = CGPointMake(@tableView.center.x, location.y)
+ UIView.commitAnimations
+
+ @tableView.beginUpdates
+ @tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimationNone)
+ @tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimationNone)
+ @delegate.gestureRecognizer(self, needsCreatePlaceholderForRowAtIndexPath: indexPath)
+ @addingIndexPath = indexPath
+ @tableView.endUpdates
+
+ @movingTimer = NSTimer.timerWithTimeInterval((1/8.0), target: self, selector: :scrollTable, userInfo: nil, repeats: true)
+ NSRunLoop.mainRunLoop.addTimer(@movingTimer, forMode: NSDefaultRunLoopMode)
+
+ when UIGestureRecognizerStateEnded
+ snapShotView = @tableView.viewWithTag(CellSnapshotTag)
+ indexPath = @addingIndexPath
+
+ @movingTimer.invalidate
+ @movingTimer = nil
+ @scrollingRate = 0
+
+ UIView.animateWithDuration(RowAnimationDuration,
+ animations: -> do
+ rect = @tableView.rectForRowAtIndexPath(indexPath)
+ snapShotView.transform = CGAffineTransformIdentity
+ snapShotView.frame = CGRectOffset(snapShotView.bounds, rect.origin.x, rect.origin.y)
+ end,
+ completion: ->(finished) do
+ snapShotView.removeFromSuperview
+
+ @tableView.beginUpdates
+ @tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimationNone)
+ @tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimationNone)
+ @delegate.gestureRecognizer(self, needsReplacePlaceholderForRowAtIndexPath: indexPath)
+ @tableView.endUpdates
+
+ @tableView.reloadVisibleRowsExceptIndexPath(indexPath)
+ @cellSnapshot = nil
+ @addingIndexPath = nil
+ @state = :none
+ end)
+
+ when UIGestureRecognizerStateChanged
+ snapShotView = @tableView.viewWithTag(CellSnapshotTag)
+ snapShotView.center = CGPointMake(@tableView.center.x, location.y)
+
+ rect = @tableView.bounds
+ location = @longPressRecognizer.locationInView(@tableView)
+ location.y -= @tableView.contentOffset.y
+
+ self.updateAddingIndexPathForCurrentLocation
+
+ topDropZoneHeight = bottomDropZoneHeight = @tableView.bounds.size.height / 6.0
+ bottomDiff = location.y - (rect.size.height - bottomDropZoneHeight)
+ if bottomDiff > 0
+ @scrollingRate = bottomDiff / (bottomDropZoneHeight / 1)
+ elsif location.y <= topDropZoneHeight
+ @scrollingRate = -(topDropZoneHeight - [location.y, 0].max) / bottomDropZoneHeight
+ else
+ @scrollingRate = 0
+ end
+ end
+ end
+
+ def gestureRecognizerShouldBegin(gestureRecognizer)
+ indexPath = indexPathFromRecognizer(gestureRecognizer)
+
+ case gestureRecognizer
+ when @panRecognizer
+ point = gestureRecognizer.translationInView(@tableView)
+ if point.y.abs > point.x.abs || indexPath.nil?
+ false
+ elsif indexPath
+ @delegate.gestureRecognizer(self, canEditRowAtIndexPath: indexPath)
+ end
+ when @longPressRecognizer
+ if indexPath
+ @delegate.gestureRecognizer(self, canMoveRowAtIndexPath: indexPath)
+ else
+ false
+ end
+ else
+ true
+ end
+ end
+
+ def tableView(aTableView, heightForRowAtIndexPath: indexPath)
+ if indexPath == @addingIndexPath && (@state == :pinching || @state == :dragging)
+ [1, @addingRowHeight || 0].max
+ else
+ @tableViewDelegate.tableView(aTableView, heightForRowAtIndexPath: indexPath)
+ end
+ end
+
+ def scrollViewDidScroll(scrollView)
+ if scrollView.contentOffset.y < 0
+ if @addingIndexPath.nil? && @state == :none && !scrollView.isDecelerating
+ @state = :dragging
+ @addingIndexPath =
+ if @delegate.respond_to?(:"gestureRecognizer:willCreateCellAtIndexPath:")
+ @delegate.gestureRecognizer(self, willCreateCellAtIndexPath: @addingIndexPath)
+ else
+ NSIndexPath.indexPathForRow(0, inSection: 0)
+ end
+
+ @tableView.beginUpdates
+ @delegate.gestureRecognizer(self, needsAddRowAtIndexPath: @addingIndexPath)
+ @tableView.insertRowsAtIndexPaths([@addingIndexPath], withRowAnimation: UITableViewRowAnimationNone)
+ @addingRowHeight = scrollView.contentOffset.y.abs
+ @tableView.endUpdates
+ end
+ end
+
+ if @state == :dragging
+ @addingRowHeight += scrollView.contentOffset.y * -1
+ @tableView.reloadData
+ scrollView.setContentOffset(CGPointZero)
+ end
+ end
+
+ def scrollViewDidEndDragging(scrollView, willDecelerate: decelerate)
+ if @state == :dragging
+ @state = :none
+ self.commitOrDiscardCell
+ end
+ end
+
+ def indexPathFromRecognizer(recognizer)
+ location = recognizer.locationInView(@tableView)
+ @tableView.indexPathForRowAtPoint(location)
+ end
+
+ def lengthForCommitEditingRowAtIndexPath(indexPath)
+ if @delegate.respond_to? :"gestureRecognizer:lengthForCommitEditingRowAtIndexPath:"
+ @delegate.gestureRecognizer(self, lengthForCommitEditingRowAtIndexPath: indexPath)
+ else
+ CommitEditingRowDefaultRowHeight
+ end
+ end
+end
+
92 GestureTable/app/transformable_cells.rb
@@ -0,0 +1,92 @@
+class TransformableTableViewCell < UITableViewCell
+ attr_accessor :finishedHeight, :tintColor
+
+ def self.transformableTableViewCellWithStyle(style, reuseIdentifier: reuseIdentifier)
+ case style
+ when :pullDown
+ PullDownTableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier: reuseIdentifier)
+ when :unfolding
+ UnfoldingTableViewCell.alloc.initWithStyle(UITableViewCellStyleSubtitle, reuseIdentifier: reuseIdentifier)
+ else
+ raise ArgumentError, "Style must be :pullDown or :unfolding"
+ end
+ end
+end
+
+class UnfoldingTableViewCell < TransformableTableViewCell
+ def initWithStyle(style, reuseIdentifier: reuseIdentifier)
+ if super
+ transform = CATransform3DIdentity
+ transform.m34 = -1 / 500.to_f
+ contentView.layer.setSublayerTransform(transform)
+
+ textLabel.layer.anchorPoint = CGPointMake(0.5, 0.0)
+ detailTextLabel.layer.anchorPoint = CGPointMake(0.5, 1.0)
+
+ textLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight
+ detailTextLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight
+
+ self.selectionStyle = UITableViewCellSelectionStyleNone
+ self.tintColor = UIColor.whiteColor
+ end
+ self
+ end
+
+ def layoutSubviews
+ super
+
+ fraction = self.frame.size.height / @finishedHeight.to_f
+ fraction = [[1, fraction].min, 0].max
+
+ angle = (Math::PI / 2) - Math.asin(fraction)
+ transform = CATransform3DMakeRotation(angle, -1, 0, 0)
+ textLabel.layer.setTransform(transform)
+ detailTextLabel.layer.setTransform(CATransform3DMakeRotation(angle, 1, 0, 0))
+
+ textLabel.backgroundColor = @tintColor.colorWithBrightness(0.3 + 0.7*fraction)
+ detailTextLabel.backgroundColor = @tintColor.colorWithBrightness(0.5 + 0.5*fraction)
+
+ contentViewSize = contentView.frame.size
+ contentViewMidY = contentViewSize.height / 2.0
+ labelHeight = @finishedHeight / 2.0
+
+ textLabel.frame = [[0, contentViewMidY - (labelHeight * fraction)], [contentViewSize.width, labelHeight + 1]]
+ detailTextLabel.frame = [[0, contentViewMidY - (labelHeight * (1 - fraction))], [contentViewSize.width, labelHeight]]
+ end
+end
+
+class PullDownTableViewCell < TransformableTableViewCell
+ def initWithStyle(style, reuseIdentifier: reuseIdentifier)
+ if super
+ transform = CATransform3DIdentity
+ transform.m34 = -1 / 500.to_f
+ contentView.layer.setSublayerTransform(transform)
+
+ textLabel.layer.anchorPoint = CGPointMake(0.5, 1.0)
+ textLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight
+
+ self.selectionStyle = UITableViewCellSelectionStyleNone
+
+ @tintColor = UIColor.whiteColor
+ end
+ self
+ end
+
+ def layoutSubviews
+ super
+
+ fraction = self.frame.size.height / @finishedHeight.to_f
+ fraction = [[1, fraction].min, 0].max
+
+ angle = (Math::PI / 2) - Math.asin(fraction)
+ transform = CATransform3DMakeRotation(angle, 1, 0, 0)
+ textLabel.layer.setTransform(transform)
+
+ textLabel.backgroundColor = @tintColor.colorWithBrightness(0.3 + 0.7*fraction)
+
+ contentViewSize = self.contentView.frame.size
+ labelHeight = @finishedHeight
+
+ self.textLabel.frame = [[0, contentViewSize.height - labelHeight], [contentViewSize.width, labelHeight]]
+ end
+end
173 GestureTable/app/view_controller.rb
@@ -0,0 +1,173 @@
+class ViewController < UITableViewController
+ AddingCell = 'Continue...'
+ DoneCell = 'Done'
+ DummyCell = 'Dummy'
+ CommittingCreateCellHeight = 60
+ NormalCellFinishingHeight = 60
+
+ def loadView
+ self.tableView = UITableView.new
+
+ @rows = ['Swipe to the right to complete', 'Swipe to left to delete', 'Drag down to create a new cell', 'Pinch two rows apart to create cell', 'Long hold to start reorder cell']
+ @grabbedObject = nil
+ @tableViewRecognizer = GestureRecognizer.alloc.initWithTableView(self.tableView, delegate:self)
+ end
+
+ def viewWillAppear(animated)
+ navigationController.setNavigationBarHidden(true, animated:false)
+ end
+
+ def viewDidLoad
+ tableView.backgroundColor = UIColor.blackColor
+ tableView.separatorStyle = UITableViewCellSeparatorStyleNone
+ tableView.rowHeight = NormalCellFinishingHeight
+ end
+
+ def numberOfSectionsInTableView(tableView)
+ 1
+ end
+
+ def tableView(tableView, numberOfRowsInSection: section)
+ @rows.count
+ end
+
+ def tableView(tableView, heightForRowAtIndexPath: indexPath)
+ NormalCellFinishingHeight
+ end
+
+ def tableView(tableView, cellForRowAtIndexPath: indexPath)
+ object = @rows[indexPath.row]
+ backgroundColor = backgroundColorForIndexPath(indexPath)
+
+ if object == AddingCell
+ if indexPath.row == 0
+ cellIdentifier = 'PullDownTableViewCell'
+ cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier)
+ unless cell
+ cell = TransformableTableViewCell.transformableTableViewCellWithStyle(:pullDown, reuseIdentifier: cellIdentifier)
+ cell.textLabel.adjustsFontSizeToFitWidth = true
+ cell.textLabel.textColor = UIColor.whiteColor
+ cell.textLabel.textAlignment = UITextAlignmentCenter
+ end
+
+ cell.tintColor = backgroundColor
+ cell.finishedHeight = CommittingCreateCellHeight
+ cell.textLabel.text = cell.frame.size.height > CommittingCreateCellHeight ? 'Release to create cell...' : 'Continue Pulling...'
+ cell.contentView.backgroundColor = UIColor.clearColor
+ else
+ cellIdentifier = 'UnfoldingTableViewCell'
+ cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier)
+ unless cell
+ cell = TransformableTableViewCell.transformableTableViewCellWithStyle(:unfolding, reuseIdentifier: cellIdentifier)
+ cell.textLabel.adjustsFontSizeToFitWidth = true
+ cell.textLabel.textColor = UIColor.whiteColor
+ cell.textLabel.textAlignment = UITextAlignmentCenter
+ end
+
+ cell.tintColor = backgroundColor
+ cell.finishedHeight = CommittingCreateCellHeight
+ cell.textLabel.text = cell.frame.size.height > CommittingCreateCellHeight ? 'Release to create cell...' : 'Continue Pinching...'
+ cell.contentView.backgroundColor = UIColor.clearColor
+ cell.detailTextLabel.text = ' '
+ end
+ else
+ cellIdentifier = 'MyCell'
+ cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier)
+ unless cell
+ cell = UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier: cellIdentifier)
+ cell.textLabel.adjustsFontSizeToFitWidth = true
+ cell.textLabel.backgroundColor = UIColor.clearColor
+ cell.selectionStyle = UITableViewCellSelectionStyleNone
+ end
+
+ text = object.to_s
+ textColor = UIColor.whiteColor
+ if object == DoneCell
+ textColor = UIColor.grayColor
+ backgroundColor = UIColor.darkGrayColor
+ elsif object == DummyCell
+ text = ''
+ backgroundColor = UIColor.clearColor
+ end
+
+ cell.textLabel.text = text
+ cell.textLabel.textColor = textColor
+ cell.contentView.backgroundColor = backgroundColor
+ end
+
+ cell
+ end
+
+ def gestureRecognizer(gestureRecognizer, needsAddRowAtIndexPath: indexPath)
+ @rows.insert(indexPath.row, AddingCell)
+ end
+
+ def gestureRecognizer(gestureRecognizer, needsCommitRowAtIndexPath: indexPath)
+ @rows[indexPath.row] = 'Added!'
+ cell = tableView.cellForRowAtIndexPath(indexPath)
+ cell.finishedHeight = NormalCellFinishingHeight
+ cell.textLabel.text = 'Just Added!'
+ end
+
+ def gestureRecognizer(gestureRecognizer, needsDiscardRowAtIndexPath: indexPath)
+ @rows.delete_at(indexPath.row)
+ end
+
+ def gestureRecognizer(gestureRecognizer, didEnterEditingState: state, forRowAtIndexPath: indexPath)
+ backgroundColor = case state
+ when :middle
+ backgroundColorForIndexPath(indexPath)
+ when :right
+ UIColor.greenColor
+ else
+ UIColor.darkGrayColor
+ end
+
+ cell = tableView.cellForRowAtIndexPath(indexPath)
+ cell.contentView.backgroundColor = backgroundColor
+ cell.tintColor = backgroundColor if cell.kind_of? TransformableTableViewCell
+ end
+
+ def gestureRecognizer(gestureRecognizer, canEditRowAtIndexPath: indexPath)
+ true
+ end
+
+ def gestureRecognizer(gestureRecognizer, commitEditingState: state, forRowAtIndexPath: indexPath)
+ tableView.beginUpdates
+ case state
+ when :left
+ @rows.delete_at(indexPath.row)
+ tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimationLeft)
+ when :right
+ @rows[indexPath.row] = DoneCell
+ tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimationLeft)
+ end
+ tableView.endUpdates
+
+ tableView.performSelector(:"reloadVisibleRowsExceptIndexPath:", withObject: indexPath, afterDelay: GestureRecognizer::RowAnimationDuration)
+ end
+
+ def gestureRecognizer(gestureRecognizer, canMoveRowAtIndexPath: indexPath)
+ true
+ end
+
+ def gestureRecognizer(gestureRecognizer, needsCreatePlaceholderForRowAtIndexPath: indexPath)
+ @grabbedObject = @rows[indexPath.row]
+ @rows[indexPath.row] = DummyCell
+ end
+
+ def gestureRecognizer(gestureRecognizer, needsMoveRowAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath)
+ object = @rows[sourceIndexPath.row]
+ @rows.delete_at(sourceIndexPath.row)
+ @rows.insert(destinationIndexPath.row, object)
+ end
+
+ def gestureRecognizer(gestureRecognizer, needsReplacePlaceholderForRowAtIndexPath: indexPath)
+ @rows[indexPath.row] = @grabbedObject
+ @grabbedObject = nil
+ end
+
+ def backgroundColorForIndexPath(indexPath)
+ UIColor.redColor.colorWithHueOffset(0.12 * indexPath.row / @rows.count)
+ end
+end
8 Hello/Rakefile
@@ -0,0 +1,8 @@
+$:.unshift('../../lib')
+require 'motion/project'
+
+Motion::Project::App.setup do |app|
+ # Use `rake config' to see complete project settings.
+ app.name = 'Hello'
+ app.icons << 'icon.png'
+end
9 Hello/app/app_delegate.rb
@@ -0,0 +1,9 @@
+class AppDelegate
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
+ @window.rootViewController = HelloViewController.alloc.init
+ @window.rootViewController.wantsFullScreenLayout = true
+ @window.makeKeyAndVisible
+ return true
+ end
+end
33 Hello/app/hello_view.rb
@@ -0,0 +1,33 @@
+class HelloView < UIView
+ def drawRect(rect)
+ if @moved
+ bgcolor = begin
+ red, green, blue = rand(100), rand(100), rand(100)
+ UIColor.colorWithRed(red/100.0, green:green/100.0, blue:blue/100.0, alpha:1.0)
+ end
+ text = "ZOMG!"
+ else
+ bgcolor = UIColor.blackColor
+ text = @touches ? "Touched #{@touches} times!" : "Hello RubyMotion!"
+ end
+
+ bgcolor.set
+ UIBezierPath.bezierPathWithRect(frame).fill
+
+ font = UIFont.systemFontOfSize(24)
+ UIColor.whiteColor.set
+ text.drawAtPoint(CGPoint.new(10, 20), withFont:font)
+ end
+
+ def touchesMoved(touches, withEvent:event)
+ @moved = true
+ setNeedsDisplay
+ end
+
+ def touchesEnded(touches, withEvent:event)
+ @moved = false
+ @touches ||= 0
+ @touches += 1
+ setNeedsDisplay
+ end
+end
5 Hello/app/hello_view_controller.rb
@@ -0,0 +1,5 @@
+class HelloViewController < UIViewController
+ def loadView
+ self.view = HelloView.alloc.init
+ end
+end
BIN  Hello/resources/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 Locations/Rakefile
@@ -0,0 +1,8 @@
+$:.unshift('../../lib')
+require 'motion/project'
+
+Motion::Project::App.setup do |app|
+ # Use `rake config' to see complete project settings.
+ app.name = 'Locations'
+ app.frameworks += ['CoreData', 'CoreLocation']
+end
11 Locations/app/app_delegate.rb
@@ -0,0 +1,11 @@
+class AppDelegate
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
+ nav = UINavigationController.alloc.initWithRootViewController(LocationsController.alloc.init)
+ nav.wantsFullScreenLayout = true
+ nav.toolbarHidden = true
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
+ @window.rootViewController = nav
+ @window.makeKeyAndVisible
+ return true
+ end
+end
21 Locations/app/location.rb
@@ -0,0 +1,21 @@
+class Location < NSManagedObject
+ def self.entity
+ @entity ||= begin
+ # Create the entity for our Location class. The entity has 3 properties. CoreData will appropriately define accessor methods for the properties.
+ entity = NSEntityDescription.alloc.init
+ entity.name = 'Location'
+ entity.managedObjectClassName = 'Location'
+ entity.properties =
+ ['creation_date', NSDateAttributeType,
+ 'latitude', NSDoubleAttributeType,
+ 'longitude', NSDoubleAttributeType].each_slice(2).map do |name, type|
+ property = NSAttributeDescription.alloc.init
+ property.name = name
+ property.attributeType = type
+ property.optional = false
+ property
+ end
+ entity
+ end
+ end
+end
66 Locations/app/locations_controller.rb
@@ -0,0 +1,66 @@
+class LocationsController < UITableViewController
+ def viewDidLoad
+ view.dataSource = view.delegate = self
+ end
+
+ def viewWillAppear(animated)
+ navigationItem.title = 'Locations'
+ navigationItem.leftBarButtonItem = editButtonItem
+ navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithBarButtonSystemItem(UIBarButtonSystemItemAdd, target:self, action:'addLocation')
+
+ # The Add button is disabled by default, and will be enabled once the location manager is ready to return the current location.
+ navigationItem.rightBarButtonItem.enabled = false
+ @location_manager ||= CLLocationManager.alloc.init.tap do |lm|
+ lm.desiredAccuracy = KCLLocationAccuracyNearestTenMeters
+ lm.startUpdatingLocation
+ lm.delegate = self
+ end
+ end
+
+ def addLocation
+ LocationsStore.shared.add_location do |location|
+ # We set up our new Location object here.
+ coordinate = @location_manager.location.coordinate
+ location.creation_date = NSDate.date
+ location.latitude = coordinate.latitude
+ location.longitude = coordinate.longitude
+ end
+ view.reloadData
+ end
+
+ def tableView(tableView, numberOfRowsInSection:section)
+ LocationsStore.shared.locations.size
+ end
+
+ CellID = 'CellIdentifier'
+ def tableView(tableView, cellForRowAtIndexPath:indexPath)
+ cell = tableView.dequeueReusableCellWithIdentifier(CellID) || UITableViewCell.alloc.initWithStyle(UITableViewCellStyleSubtitle, reuseIdentifier:CellID)
+ location = LocationsStore.shared.locations[indexPath.row]
+
+ @date_formatter ||= NSDateFormatter.alloc.init.tap do |df|
+ df.timeStyle = NSDateFormatterMediumStyle
+ df.dateStyle = NSDateFormatterMediumStyle
+ end
+ cell.textLabel.text = @date_formatter.stringFromDate(location.creation_date)
+ cell.detailTextLabel.text = "%0.3f, %0.3f" % [location.latitude, location.longitude]
+ cell
+ end
+
+ def tableView(tableView, editingStyleForRowAtIndexPath:indexPath)
+ UITableViewCellEditingStyleDelete
+ end
+
+ def tableView(tableView, commitEditingStyle:editingStyle, forRowAtIndexPath:indexPath)
+ location = LocationsStore.shared.locations[indexPath.row]
+ LocationsStore.shared.remove_location(location)
+ tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation:UITableViewRowAnimationFade)
+ end
+
+ def locationManager(manager, didUpdateToLocation:newLocation, fromLocation:oldLocation)
+ navigationItem.rightBarButtonItem.enabled = true
+ end
+
+ def locationManager(manager, didFailWithError:error)
+ navigationItem.rightBarButtonItem.enabled = false
+ end
+end
61 Locations/app/locations_store.rb
@@ -0,0 +1,61 @@
+class LocationsStore
+ def self.shared
+ # Our store is a singleton object.
+ @shared ||= LocationsStore.new
+ end
+
+ def locations
+ @locations ||= begin
+ # Fetch all locations from the model, sorting by the creation date.
+ request = NSFetchRequest.alloc.init
+ request.entity = NSEntityDescription.entityForName('Location', inManagedObjectContext:@context)
+ request.sortDescriptors = [NSSortDescriptor.alloc.initWithKey('creation_date', ascending:false)]
+
+ error_ptr = Pointer.new(:object)
+ data = @context.executeFetchRequest(request, error:error_ptr)
+ if data == nil
+ raise "Error when fetching data: #{error_ptr[0].description}"
+ end
+ data
+ end
+ end
+
+ def add_location
+ # Yield a blank, newly created Location entity, then save the model.
+ yield NSEntityDescription.insertNewObjectForEntityForName('Location', inManagedObjectContext:@context)
+ save
+ end
+
+ def remove_location(location)
+ # Delete the given entity, then save the model.
+ @context.deleteObject(location)
+ save
+ end
+
+ private
+
+ def initialize
+ # 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.
+ model = NSManagedObjectModel.alloc.init
+ model.entities = [Location.entity]
+
+ store = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(model)
+ store_url = NSURL.fileURLWithPath(File.join(NSHomeDirectory(), 'Documents', 'Locations.sqlite'))
+ error_ptr = Pointer.new(:object)
+ unless store.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:store_url, options:nil, error:error_ptr)
+ raise "Can't add persistent SQLite store: #{error_ptr[0].description}"
+ end
+
+ context = NSManagedObjectContext.alloc.init
+ context.persistentStoreCoordinator = store
+ @context = context
+ end
+
+ def save
+ error_ptr = Pointer.new(:object)
+ unless @context.save(error_ptr)
+ raise "Error when saving the model: #{error_ptr[0].description}"
+ end
+ @locations = nil
+ end
+end
10 Mustache/Rakefile
@@ -0,0 +1,10 @@
+$:.unshift('../../lib')
+require 'motion/project'
+
+#ENV['debug'] ||= '0' # Because the REPL cannot be loaded with CoreImage yet.
+
+Motion::Project::App.setup do |app|
+ # Use `rake config' to see complete project settings.
+ app.name = 'Mustache'
+ app.frameworks += ['QuartzCore', 'CoreImage']
+end
9 Mustache/app/app_delegate.rb
@@ -0,0 +1,9 @@
+class AppDelegate
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
+ @window.rootViewController = MustacheViewController.alloc.init
+ @window.rootViewController.wantsFullScreenLayout = true
+ @window.makeKeyAndVisible
+ return true
+ end
+end
99 Mustache/app/mustache_view_controller.rb
@@ -0,0 +1,99 @@
+class MustacheViewController < UIViewController
+ def loadView
+ self.view = UIImageView.alloc.init
+ @debug_face = false # Set to true to debug face features.
+ end
+
+ def viewDidLoad
+ @images = %w{matz guido kay jmccolor}.map { |name| UIImage.imageNamed(name + '.jpg') }
+ view.image = @images.first
+ view.contentMode = UIViewContentModeScaleAspectFit
+ view.userInteractionEnabled = true
+
+ previousGesture = UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'swipePreviousGesture:')
+ previousGesture.direction = UISwipeGestureRecognizerDirectionLeft
+ view.addGestureRecognizer(previousGesture)
+ nextGesture = UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'swipeNextGesture:')
+ nextGesture.direction = UISwipeGestureRecognizerDirectionRight
+ view.addGestureRecognizer(nextGesture)
+ end
+
+ def viewDidAppear(animated)
+ mustachify
+ end
+
+ def mustachify
+ # Remove previous mustaches.
+ view.subviews.each { |v| v.removeFromSuperview }
+
+ # CoreImage used a coordinate system which is flipped on the Y axis
+ # compared to UIKit. Also, a UIImageView can return an image larger than
+ # itself. To properly translate points, we use an affine transform.
+ transform = CGAffineTransformMakeScale(view.bounds.size.width / view.image.size.width, -1 * (view.bounds.size.height / view.image.size.height))
+ transform = CGAffineTransformTranslate(transform, 0, -view.image.size.height)
+
+ image = CIImage.imageWithCGImage(view.image.CGImage)
+ @detector ||= CIDetector.detectorOfType CIDetectorTypeFace, context:nil, options: { CIDetectorAccuracy: CIDetectorAccuracyHigh }
+ @detector.featuresInImage(image).each do |feature|
+ # We need the mouth and eyes positions to determine where the mustache
+ # should be added.
+ next unless feature.hasMouthPosition and feature.hasLeftEyePosition and feature.hasRightEyePosition
+
+ if @debug_face
+ [feature.leftEyePosition,feature.rightEyePosition,feature.mouthPosition].each do |pt|
+ v = UIView.alloc.initWithFrame CGRectMake(0, 0, 20, 20)
+ v.backgroundColor = UIColor.greenColor.colorWithAlphaComponent(0.2)
+ pt = CGPointApplyAffineTransform(pt, transform)
+ v.center = pt
+ view.addSubview(v)
+ end
+ end
+
+ # Create the mustache view.
+ mustacheView = UIImageView.alloc.init
+ mustacheView.image = UIImage.imageNamed('mustache')
+ mustacheView.contentMode = UIViewContentModeScaleAspectFit
+
+ # Compute its location and size, based on the position of the eyes and
+ # mouth.
+ w = feature.bounds.size.width
+ h = feature.bounds.size.height / 5
+ x = (feature.mouthPosition.x + (feature.leftEyePosition.x + feature.rightEyePosition.x) / 2) / 2 - w / 2
+ y = feature.mouthPosition.y
+ mustacheView.frame = CGRectApplyAffineTransform([[x, y], [w, h]], transform)
+
+ # Apply a rotation on the mustache, based on the face inclination.
+ mustacheAngle = Math.atan2(feature.leftEyePosition.x - feature.rightEyePosition.x, feature.leftEyePosition.y - feature.rightEyePosition.y) + Math::PI/2
+ mustacheView.transform = CGAffineTransformMakeRotation(mustacheAngle)
+
+ view.addSubview(mustacheView)
+ end
+ end
+
+ def shouldAutorotateToInterfaceOrientation(*)
+ mustachify
+ true
+ end
+
+ def swipePreviousGesture(gesture)
+ idx = @images.index(view.image)
+ view.image =
+ if idx == 0
+ @images.last
+ else
+ @images[idx - 1]
+ end
+ mustachify
+ end
+
+ def swipeNextGesture(gesture)
+ idx = @images.index(view.image)
+ view.image =
+ if idx == @images.size - 1
+ @images.first
+ else
+ @images[idx + 1]
+ end
+ mustachify
+ end
+end
BIN  Mustache/resources/guido.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Mustache/resources/jmccolor.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Mustache/resources/kay.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Mustache/resources/matz.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Mustache/resources/mustache.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 Paint/Rakefile
@@ -0,0 +1,8 @@
+$:.unshift('../../lib')
+require 'motion/project'
+
+Motion::Project::App.setup do |app|
+ # Use `rake config' to see complete project settings.
+ app.name = 'Paint'
+ app.frameworks << 'AVFoundation'
+end
9 Paint/app/app_delegate.rb
@@ -0,0 +1,9 @@
+class AppDelegate
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
+ @window.rootViewController = PaintViewController.alloc.init
+ @window.rootViewController.wantsFullScreenLayout = true
+ @window.makeKeyAndVisible
+ return true
+ end
+end
57 Paint/app/paint_view.rb
@@ -0,0 +1,57 @@
+class PaintView < UIView
+ def initWithFrame(ect)
+ if super
+ path = NSBundle.mainBundle.pathForResource('erase', ofType:'caf')
+ url = NSURL.fileURLWithPath(path)
+ error_ptr = Pointer.new(:id)
+ @eraseSound = AVAudioPlayer.alloc.initWithContentsOfURL(url,
+ error:error_ptr)
+ unless @eraseSound
+ raise "Can't open sound file: #{error_ptr[0].description}"
+ end
+ @paths = []
+ end
+ self
+ end
+
+ def drawRect(rect)
+ UIColor.blackColor.set
+ UIBezierPath.bezierPathWithRect(rect).fill
+ @paths.each do |path, color|
+ color.set
+ path.stroke
+ end
+ end
+
+ def touchesBegan(touches, withEvent:event)
+ bp = UIBezierPath.alloc.init
+ bp.lineWidth = 3.0
+ random_color = begin
+ red, green, blue = rand(100)/100.0, rand(100)/100.0, rand(100)/100.0
+ UIColor.alloc.initWithRed(red/100.0, green:green, blue:blue, alpha:1.0)
+ end
+ @paths << [bp, random_color]
+ end
+
+ def touchesMoved(touches, withEvent:event)
+ touch = event.touchesForView(self).anyObject
+ point = touch.locationInView(self)
+ if @previousPoint and !@paths.empty?
+ bp = @paths.last.first
+ bp.moveToPoint(@previousPoint)
+ bp.addLineToPoint(point)
+ end
+ @previousPoint = point
+ setNeedsDisplay
+ end
+
+ def touchesEnded(touches, withEvent:event)
+ @previousPoint = nil
+ end
+
+ def eraseContent
+ @paths.clear
+ @eraseSound.play
+ setNeedsDisplay
+ end
+end
13 Paint/app/paint_view_controller.rb
@@ -0,0 +1,13 @@
+class PaintViewController < UIViewController
+ def loadView
+ self.view = PaintView.alloc.init
+ end
+
+ def canBecomeFirstResponder
+ true
+ end
+
+ def motionEnded(motion, withEvent:event)
+ self.view.eraseContent if motion == UIEventSubtypeMotionShake
+ end
+end
BIN  Paint/resources/erase.caf
Binary file not shown
2  Trollify/README
@@ -0,0 +1,2 @@
+This example was contributed by Johannes Fahrenkrug, http://springenwerk.com
+The app/uiimage_extension.rb file is derived from code by Hardy Macia, Catamount Software.
9 Trollify/Rakefile
@@ -0,0 +1,9 @@
+$:.unshift('../../lib')
+require 'motion/project'
+
+Motion::Project::App.setup do |app|
+ # Use `rake config' to see complete project settings.
+ app.name = 'trollify'
+ app.frameworks += ['QuartzCore', 'AssetsLibrary', 'MobileCoreServices']
+ app.icons = ['icon.png', 'icon@2x.png']
+end
9 Trollify/app/app_delegate.rb
@@ -0,0 +1,9 @@
+class AppDelegate
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
+ @window.rootViewController = UINavigationController.alloc.initWithRootViewController(ImageCompositionController.alloc.init)
+ @window.rootViewController.wantsFullScreenLayout = true
+ @window.makeKeyAndVisible
+ return true
+ end
+end
193 Trollify/app/image_composition_controller.rb
@@ -0,0 +1,193 @@
+class ImageCompositionController < UIViewController
+ def loadView
+ self.view = UIImageView.alloc.init
+ view.contentMode = UIViewContentModeScaleAspectFill
+ view.userInteractionEnabled = true
+
+ view.image = UIImage.imageNamed("ballmer.jpg")
+ view.addSubview(troll_image_view)
+
+ rotationGesture = UIRotationGestureRecognizer.alloc.initWithTarget(self, action: 'rotate_image:')
+ troll_image_view.addGestureRecognizer(rotationGesture)
+
+ panGesture = UIPanGestureRecognizer.alloc.initWithTarget(self, action: 'pan_image:')
+ panGesture.maximumNumberOfTouches = 2
+ panGesture.delegate = self
+ troll_image_view.addGestureRecognizer(panGesture)
+
+ pinchGesture = UIPinchGestureRecognizer.alloc.initWithTarget(self, action:'scale_image:')
+ pinchGesture.delegate = self
+ troll_image_view.addGestureRecognizer(pinchGesture)
+
+ navigationItem.leftBarButtonItem = UIBarButtonItem.alloc.initWithTitle("Pick", style:UIBarButtonItemStylePlain, target:self, action:'show_source_sheet')
+ navigationItem.rightBarButtonItem = UIBarButtonItem.alloc.initWithTitle("Save", style:UIBarButtonItemStylePlain, target:self, action:'save_image')
+ end
+
+ def viewWillAppear(animated)
+ navigationController.setNavigationBarHidden(false, animated:true)
+ troll_image_view.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))
+ end
+
+ def troll_image_view
+ @troll_image_view ||= begin
+ image = UIImageView.alloc.init
+ image.image = UIImage.imageNamed("trollface.png")
+ image.frame = CGRectMake(0, 0, image.image.size.width, image.image.size.height)
+ image.userInteractionEnabled = true
+
+ # Set initial scale of the trollface to a quarter.
+ image.transform = CGAffineTransformMakeScale(0.25, 0.25)
+ image
+ end
+ end
+
+ # Scale and rotation transforms are applied relative to the layer's anchor point.
+ # This method moves a gesture recognizer's view's anchor point between the user's fingers.
+ def adjust_anchor_point_for_gesture_recognizer(gestureRecognizer)
+ if gestureRecognizer.state == UIGestureRecognizerStateBegan
+ locationInView = gestureRecognizer.locationInView(troll_image_view)
+ locationInSuperview = gestureRecognizer.locationInView(troll_image_view.superview)
+
+ troll_image_view.layer.anchorPoint = CGPointMake(locationInView.x / troll_image_view.bounds.size.width, locationInView.y / troll_image_view.bounds.size.height)
+ troll_image_view.center = locationInSuperview
+ end
+ end
+
+ def rotate_image(gestureRecognizer)
+ adjust_anchor_point_for_gesture_recognizer(gestureRecognizer)
+ if gestureRecognizer.state == UIGestureRecognizerStateBegan || gestureRecognizer.state == UIGestureRecognizerStateChanged
+ gestureRecognizer.view.transform = CGAffineTransformRotate(gestureRecognizer.view.transform, gestureRecognizer.rotation)
+ gestureRecognizer.rotation = 0
+ end
+ end
+
+ def pan_image(gestureRecognizer)
+ adjust_anchor_point_for_gesture_recognizer(gestureRecognizer)
+ if gestureRecognizer.state == UIGestureRecognizerStateBegan || gestureRecognizer.state == UIGestureRecognizerStateChanged
+ translation = gestureRecognizer.translationInView(troll_image_view.superview)
+ troll_image_view.center = CGPointMake(troll_image_view.center.x + translation.x, troll_image_view.center.y + translation.y)
+ gestureRecognizer.setTranslation(CGPointZero, inView:troll_image_view.superview)
+ end
+ end
+
+ def scale_image(gestureRecognizer)
+ adjust_anchor_point_for_gesture_recognizer(gestureRecognizer)
+ if gestureRecognizer.state == UIGestureRecognizerStateBegan || gestureRecognizer.state == UIGestureRecognizerStateChanged
+ gestureRecognizer.view.transform = CGAffineTransformScale(gestureRecognizer.view.transform, gestureRecognizer.scale, gestureRecognizer.scale)
+ gestureRecognizer.scale = 1
+ end
+ end
+
+ def gestureRecognizer(gestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer)
+ true
+ end
+
+ def show_source_sheet
+ popupQuery = UIActionSheet.alloc.initWithTitle("", delegate:self, cancelButtonTitle:'Cancel', destructiveButtonTitle:nil, otherButtonTitles:"Choose Existing", "Take Picture", nil)
+ popupQuery.delegate = self
+ popupQuery.actionSheetStyle = UIActionSheetStyleBlackOpaque
+ popupQuery.showInView(view)
+ end
+
+ def actionSheet(actionSheet, clickedButtonAtIndex:buttonIndex)
+ case buttonIndex
+ when 0
+ pick_image
+ when 1
+ take_image
+ when 2
+ # cancelled
+ end
+ end
+
+ def pick_image
+ pick_image_with_source(UIImagePickerControllerSourceTypePhotoLibrary)
+ end
+
+ def take_image
+ pick_image_with_source(UIImagePickerControllerSourceTypeCamera)
+ end
+
+ def pick_image_with_source(source_type)
+ # Create and show the image picker.
+ imagePicker = UIImagePickerController.alloc.init
+ imagePicker.sourceType = source_type
+ imagePicker.mediaTypes = [KUTTypeImage]
+ imagePicker.delegate = self
+ imagePicker.allowsImageEditing = false
+ presentModalViewController(imagePicker, animated:true)
+ end
+
+ # UIImagePickerControllerDelegate methods
+
+ def imagePickerControllerDidCancel(picker)
+ dismissModalViewControllerAnimated(true)
+ end
+
+ def imagePickerController(picker, didFinishPickingMediaWithInfo:info)
+ mediaType = info[UIImagePickerControllerMediaType]
+ if mediaType == KUTTypeImage
+ editedImage = info[UIImagePickerControllerEditedImage]
+ originalImage = info[UIImagePickerControllerOriginalImage]
+ view.image = editedImage || originalImage
+ end
+ dismissModalViewControllerAnimated(true)
+ end
+
+ def save_image
+ image = view.image
+
+ troll_image_transform = troll_image_view.transform
+ trollfaceRotation = Math.atan2(troll_image_transform.b, troll_image_transform.a)
+ trollfaceScaleX = Math.sqrt(troll_image_transform.a**2 + troll_image_transform.c**2)
+ trollfaceScaleY = Math.sqrt(troll_image_transform.b**2 + troll_image_transform.d**2)
+
+ # We are displaying the image in AspectFill mode. This will give us the scale.
+ scale = [image.size.width/view.frame.size.width, image.size.height/view.frame.size.height].min
+
+ targetWidth = image.size.width
+ targetHeight = image.size.height
+
+ # Image Rect and Image Context in the size of the image.
+ imageRect = CGRectMake(0, 0, image.size.width, image.size.height)
+
+ UIGraphicsBeginImageContextWithOptions(CGSizeMake(targetWidth, targetHeight), true, UIScreen.mainScreen.scale)
+ c = UIGraphicsGetCurrentContext()
+ image.drawInRect(imageRect)
+
+ # Potential landscape rotation.
+ CGContextSaveGState(c)
+
+ # Get the (possibly rotated) image.
+ trollface_image = troll_image_view.image
+ if trollfaceRotation != 0
+ trollface_image = troll_image_view.image.imageRotatedByRadians(trollfaceRotation)
+ end
+
+ # Move the context down and left (or either or) to match where the trollface view was on the screen
+ CGContextTranslateCTM(c, (image.size.width-view.frame.size.width*scale)/2, (image.size.height-view.frame.size.height*scale)/2)
+ # Scale the coordinate context,
+ CGContextScaleCTM(c, scale, scale)
+
+ # Draw the trollface image, correctly scaled.
+ trollface_image.drawInRect(CGRectMake(troll_image_view.frame.origin.x, troll_image_view.frame.origin.y, trollface_image.size.width * trollfaceScaleX, trollface_image.size.height * trollfaceScaleY))
+
+ CGContextRestoreGState(c)
+
+ newImage = UIGraphicsGetImageFromCurrentImageContext() # UIImage returned
+
+ UIGraphicsEndImageContext()
+
+ # Save to camera roll.
+ library = ALAssetsLibrary.alloc.init
+ library.writeImageToSavedPhotosAlbum(newImage.CGImage, orientation:ALAssetOrientationUp, completionBlock: lambda do |assetURL, error|
+ if error
+ alert = UIAlertView.alloc.init
+ alert.title = "Error When Saving Picture"
+ alert.message = error.localizedDescription
+ alert.addButtonWithTitle('OK')
+ alert.show
+ end
+ end)
+ end
+end
27 Trollify/app/uiimage_extension.rb
@@ -0,0 +1,27 @@
+class UIImage
+ def imageRotatedByRadians(radians)
+ # Calculate the size of the rotated view's containing box for our drawing space.
+ rotatedViewBox = UIView.alloc.initWithFrame(CGRectMake(0,0,self.size.width, self.size.height))
+ t = CGAffineTransformMakeRotation(radians)
+ rotatedViewBox.transform = t
+ rotatedSize = rotatedViewBox.frame.size
+
+ # Create the bitmap context.
+ UIGraphicsBeginImageContext(rotatedSize)
+ bitmap = UIGraphicsGetCurrentContext()
+
+ # Move the origin to the middle of the image so we will rotate and scale around the center.
+ CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2)
+
+ # Rotate the image context.
+ CGContextRotateCTM(bitmap, radians)
+
+ # Now, draw the rotated/scaled image into the context.
+ CGContextScaleCTM(bitmap, 1.0, -1.0)
+ CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), self.CGImage)
+
+ newImage = UIGraphicsGetImageFromCurrentImageContext()
+ UIGraphicsEndImageContext()
+ newImage
+ end
+end
BIN  Trollify/resources/ballmer.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Trollify/resources/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Trollify/resources/icon@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  Trollify/resources/trollface.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 Trollify/spec/main_spec.rb
@@ -0,0 +1,9 @@
+describe "Application 'trollify'" do
+ before do
+ @app = UIApplication.sharedApplication
+ end
+
+ it "has one window" do
+ @app.windows.size.should == 1
+ end
+end
7 Tweets/Rakefile
@@ -0,0 +1,7 @@
+$:.unshift('../../lib')
+require 'motion/project'
+
+Motion::Project::App.setup do |app|
+ # Use `rake config' to see complete project settings.
+ app.name = 'Tweets'
+end
9 Tweets/app/app_delegate.rb
@@ -0,0 +1,9 @@
+class AppDelegate
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.applicationFrame)
+ @window.rootViewController = TweetsController.alloc.initWithStyle(UITableViewStylePlain)
+ @window.rootViewController.wantsFullScreenLayout = true
+ @window.makeKeyAndVisible
+ return true
+ end
+end
11 Tweets/app/tweet.rb
@@ -0,0 +1,11 @@
+class Tweet
+ attr_reader :author, :message, :profile_image_url
+ attr_accessor :profile_image
+
+ def initialize(dict)
+ @author = dict['from_user_name']
+ @message = dict['text']
+ @profile_image_url = dict['profile_image_url']
+ @profile_image = nil
+ end
+end
51 Tweets/app/tweet_cell.rb
@@ -0,0 +1,51 @@
+class TweetCell < UITableViewCell
+ CellID = 'CellIdentifier'
+ MessageFontSize = 14
+
+ def self.cellForTweet(tweet, inTableView:tableView)
+ cell = tableView.dequeueReusableCellWithIdentifier(TweetCell::CellID) || TweetCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:CellID)
+ cell.fillWithTweet(tweet, inTableView:tableView)
+ cell
+ end
+
+ def initWithStyle(style, reuseIdentifier:cellid)
+ if super
+ self.textLabel.numberOfLines = 0
+ self.textLabel.font = UIFont.systemFontOfSize(MessageFontSize)
+ end
+ self
+ end
+
+ def fillWithTweet(tweet, inTableView:tableView)
+ self.textLabel.text = tweet.message
+
+ unless tweet.profile_image
+ self.imageView.image = nil
+ Dispatch::Queue.concurrent.async do
+ profile_image_data = NSData.alloc.initWithContentsOfURL(NSURL.URLWithString(tweet.profile_image_url))
+ if profile_image_data
+ tweet.profile_image = UIImage.alloc.initWithData(profile_image_data)
+ Dispatch::Queue.main.sync do
+ self.imageView.image = tweet.profile_image
+ tableView.delegate.reloadRowForTweet(tweet)
+ end
+ end
+ end
+ else
+ self.imageView.image = tweet.profile_image
+ end
+ end
+
+ def self.heightForTweet(tweet, width)
+ constrain = CGSize.new(width - 57, 1000)
+ size = tweet.message.sizeWithFont(UIFont.systemFontOfSize(MessageFontSize), constrainedToSize:constrain)
+ [57, size.height + 8].max
+ end
+
+ def layoutSubviews
+ super
+ self.imageView.frame = CGRectMake(2, 2, 49, 49)
+ label_size = self.frame.size
+ self.textLabel.frame = CGRectMake(57, 0, label_size.width - 59, label_size.height)
+ end
+end
77 Tweets/app/tweets_controller.rb
@@ -0,0 +1,77 @@
+class TweetsController < UITableViewController
+ def viewDidLoad
+ @tweets = []
+ searchBar = UISearchBar.alloc.initWithFrame(CGRectMake(0, 0, self.tableView.frame.size.width, 0))
+ searchBar.delegate = self;
+ searchBar.showsCancelButton = true;
+ searchBar.sizeToFit
+ view.tableHeaderView = searchBar
+ view.dataSource = view.delegate = self
+
+ searchBar.text = 'xcode crash'
+ searchBarSearchButtonClicked(searchBar)
+ end
+
+ def searchBarSearchButtonClicked(searchBar)
+ query = searchBar.text.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
+ url = "http://search.twitter.com/search.json?q=#{query}"
+
+ @tweets.clear
+ Dispatch::Queue.concurrent.async do
+ error_ptr = Pointer.new(:object)
+ data = NSData.alloc.initWithContentsOfURL(NSURL.URLWithString(url), options:NSDataReadingUncached, error:error_ptr)
+ unless data
+ presentError error_ptr[0]
+ return
+ end
+ json = NSJSONSerialization.JSONObjectWithData(data, options:0, error:error_ptr)
+ unless json
+ presentError error_ptr[0]
+ return
+ end
+
+ new_tweets = []
+ json['results'].each do |dict|
+ new_tweets << Tweet.new(dict)
+ end
+
+ Dispatch::Queue.main.sync { load_tweets(new_tweets) }
+ end
+
+ searchBar.resignFirstResponder
+ end
+
+ def searchBarCancelButtonClicked(searchBar)
+ searchBar.resignFirstResponder
+ end
+
+ def load_tweets(tweets)
+ @tweets = tweets
+ view.reloadData
+ end
+
+ def presentError(error)
+ # TODO
+ $stderr.puts error.description
+ end
+
+ def tableView(tableView, numberOfRowsInSection:section)
+ @tweets.size
+ end
+
+ def tableView(tableView, heightForRowAtIndexPath:indexPath)
+ TweetCell.heightForTweet(@tweets[indexPath.row], tableView.frame.size.width)
+ end
+
+ def tableView(tableView, cellForRowAtIndexPath:indexPath)
+ tweet = @tweets[indexPath.row]
+ TweetCell.cellForTweet(tweet, inTableView:tableView)
+ end
+
+ def reloadRowForTweet(tweet)
+ row = @tweets.index(tweet)
+ if row
+ view.reloadRowsAtIndexPaths([NSIndexPath.indexPathForRow(row, inSection:0)], withRowAnimation:false)
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.