Академический Документы
Профессиональный Документы
Культура Документы
RubyMotion is a commercial iOS development platform using ruby, which adheres to apple appstore specification it is built by Laurent Sansonetti . In RubyMotion Cookbook I will present solutions for common tasks for building iOS apps, this is based on code published on open source projects, tutorials, porting native apps to RubyMotion. You can post your questions / problems / suggestions / sample code / corrections on this link given below I would try to get a solution and also add it to this book and the accompanying code. Every contribution would be attributed. https://github.com/railsfactory/rubymotion-cookbook/issues
RubyMotion Cookbook
Installation
I am assuming you already have a mac with OSX Lion and App Store installed. Open the app store, search for xcode, click on it, it is a free app , download it.
Xcode Install
This is a huge file about 1.5GB, so you need to a good connection and have enough time to download in a single stretch. Make sure you have couple of GB's of free space. Xcode gets installed in /Developer in your hard disk/SSD. For running the app built by you on device you would need a Apple developer account, I would add the instructions at a later time.
RubyMotion install
Ruby comes preinstalled in OSX Lion, open terminal and check the version.
$ ruby -v ruby 1.8.7 (2010-01-10 patchlevel 249) [universal-darwin11.0]
If you are a advanced user you can get RVM and install a custom ruby version. Goto RubyMotion site and click on the buy link and buy your RubyMotion License. Rates as on 9th May is 149$ it is a early bird rate so hurry. Download the installer http://www.rubymotion.com/files/RubyMotion%20Installer.zip . Please have your license code ready as it asks at this stage. Open the zip file and run the installer. Accept the terms and conditions, enter the license key and begin install, you would be prompted for your system password, once that is done in couple of seconds the install would complete and you are ready to work with RubyMotion.
Installation
Getting Started
Your Mac is ready to run RubyMotion, but it is a good idea to view initial screencasts which make understand the concepts in a visual way
Forum
this is the place to go and find solutions for your problems, RubyMotion Google Group Most participants are early adopters, just like you and good discussions happen, please be polite and somebody would try to help you resolve your issues. Bonus, You can make feature requests as well. Just remember this is a closed/moderated group.
Getting Started
Motion command
let us run motion and know what this command can do
$ motion Usage: motion [-h, --help] motion [-v, --version] motion <command> [<args...>] Commands: create activate update support
Create a new project Activate the software license Update the software Create a support ticket
Hello World
It is obligatory to build a Hello World app in every technology/language
$ motion create hello Create hello Create hello/.gitignore Create hello/Rakefile Create hello/app Create hello/app/app_delegate.rb Create hello/resources Create hello/spec Create hello/spec/main_spec.rb
Our project is created and at this stage we can run it on our iOS simulator
$ cd hello $ rake Build Compile Create Link Create Create Create Simulate
2012-05-10 23:54:08.743 hello[29464:f803] Applications are expected to have a root view controller at the end of appl (main)>>
Just ran one command and voila the iOS Simulator is running in iPhone mode and a blank screen is running. Actually your application was compiled, copied to the simulator and executed on the simulator.
How it works
now you know the code works, what code boilerplate has been added WIP
Did you see the message is now displayed on your simulator. Can we change the message to something else, easy
(main)>> hello.message = "Thanks Laurent and Matz" => "Thanks Laurent and Matz"
=> hello.methods => [:"addTextFieldWithValue:label:", :titleLabel, :canBecomeFirstResponder, :numberOfRows, :table, :keyboard, :textField, :"setSubtitle:", :subtitle, :tableView, :dismiss, :"initWithTitle:buttons:defaultButtonIndex:delegate:context:", :"setNumberOfRows:", :"popupAlertAnimated:", :"setDimView:", :"setTitleMaxLineCount:", :"setRunsModal:", :"setBodyText:", :"setAlertSheetStyle:", :"buttonAtIndex:", :requiresPortraitOrientation, :textFieldCount, :buttons, :"layoutAnimated:", :"dismissAnimated:", :"popupAlertAnimated:atOffset:", :"presentSheetFromAboveView:", :"presentSheetFromBehindView:", :groupsTextFields, :"setGroupsTextFields:", :"setTaglineText:", :bodyText, :titleMaxLineCount, :"setBodyTextMaxLineCount:", :bodyMaxLineCount, :"setDefaultButton:", :defaultButton, :"setDestructiveButton:", :destructiveButton, :"addButtonWithTitle:label:", :"addButtonWithTitle:buttonClass:", :buttonCount, :"setTableShouldShowMinimumContent:", :tableShouldShowMinimumContent, :"setShowsOverSpringBoardAlerts:", :showsOverSpringBoardAlerts, :isBodyTextTruncated, :"presentSheetInView:", :"presentSheetToAboveView:", :backgroundSize, :alertSheetStyle, :"setDimsBackground:", :dimsBackground, :"setSuspendTag:", :suspendTag, :"setBlocksInteraction:", :blocksInteraction, :runsModal, :titleRect, :numberOfLinesInTitle, :"presentSheetFromButtonBar:", :"replaceAlert:", :"popupAlertAnimated:animationType:atOffset:", :"popupAlertAnimated:animationType:", :"setKeyboardShowsOnPopup:", :bodyTextLabel, :taglineTextLabel, :"setForc :forceHorizontalButtonsLayout, :resignFirstResponder, :becomeFirstResponder, :layout, :context, :"setContext:", :"initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:", :"showWithAnimationType:", :show, :cancelButtonIndex, :"dismissWithClickedButtonIndex:animated:", :isVisible, :"addButtonWithTitle:", :"setCancelButtonIndex:", :numberOfButtons, :"buttonTitleAtIndex:", :"textFieldAtIndex:", :"setMessage:", :"setDefaultButtonIndex:", :"initWithTitle:message:delegate:defaultButton:cancelButton:otherButtons:", :defaultButtonIndex, :firstOtherButtonIndex, :"setAlertViewStyle:", :alertViewStyle, :dealloc, :"initWithFrame:", :"setTitle:", :title, :"setDelegate:", :delegate, :message, :"resizeForKeyplaneSize:",
:"drawRect:forViewPrintFormatter:", :viewPrintFormatter, :textInputView, :isAccessibilityElementByDefault, :isElementAccessibilityExposedToInterfaceBuilder, :textEffectsVisibilityLevel, :textEffectsVisibilityLevelWhenKey, :"compareTextEffectsOrdering:", :needsWebDocumentViewEventsDirectly, :"reduceWidth:", :"gestureEnded:", :"setGestureDelegate:", :"setEnabledGestures:", :"setValue:forGestureAttribute:", :"gestureStarted:", :"gestureChanged:", :"stateForGestureType:", :"animator:stopAnimation:", :"animator:startAnimation:", :"zoomToScale:", :"rotateToDegrees:", :canHandleGestures, :gestureDelegate, :enabledGestures, :"valueForGestureAttribute:", :"setRotationDegrees:duration:", :rotationDegrees, :"endEditing:", :useBlockyMagnificationInClassic, :scriptingInfoWithChildren, :recursiveDescription, :description, :"layoutSublayersOfLayer:", :"actionForLayer:forKey:", :"drawLayer:inContext:", :"setValue:forKey:", :gestureRecognizers, :removeAllGestureRecognizers, :"removeGestureRecognizer:", :"setGestureRecognizers:", :gesturesEnabled, :"setGesturesEnabled:", :deliversTouchesForGesturesToSuperview, :"setDeliversTouchesForGesturesToSuperview:", :"addGestureRecognizer:", :viewTraversalMark, :isInAnimatedVCTransition, :"setInAnimatedVCTransition:", :"setViewTraversalMark:", :skipsSubviewEnumeration, :"setSkipsSubviewEnumeration:", :frameOrigin, :"setFrameOrigin:", :"pointInside:forEvent:", :"hitTest:forEvent:", :"initWithSize:", :"setClipsSubviews:", :"createSnapshotWithRect:", :"setEnabled:", :isEnabled, :"setSize:", :size, :"addAnimation:forKey:", :"setNeedsDisplayOnBoundsChange:", :"setAlpha:", :"setContentScaleFactor:", :"setClearsContextBeforeDrawing:", :"setContentMode:", :"setContentStretch:", :"setClipsToBounds:", :clearsContextBeforeDrawing, :contentMode, :contentStretch, :clipsToBounds, :contentScaleFactor, :"setClearsContext:", :recursivelyForceDisplayIfNeeded, :visibleBounds, :"setFixedBackgroundPattern:", :isHiddenOrHasHiddenAncestor, :"setContentsPosition:", :"newSnapshotWithRect:", :forceDisplayIfNeeded, :alpha, :needsDisplay, :needsDisplayOnBoundsChange, :"setBackgroundColor:", :backgroundColor, :"setHidden:", :isHidden, :"setOpaque:", :isOpaque, :setNeedsDisplay, :"drawRect:", :"setNeedsDisplayInRect:", :didMoveToWindow, :layoutSubviews, :"sendSubviewToBack:", :"bringSubviewToFront:", :"containsView:", :layoutBelowIfNeeded, :"willMoveToWindow:", :deferredBecomeFirstResponder, :"movedFromSuperview:", :didMoveToSuperview, :"viewWithTag:", :"insertSubview:atIndex:", :"exchangeSubviewAtIndex:withSubviewAtIndex:", :"insertSubview:belowSubview:", :"insertSubview:aboveSubview:", :"didAddSubview:", :"willMoveToSuperview:", :"insertSubview:below:", :"insertSubview:above:", :"movedToSuperview:", :"viewWillMoveToSuperview:", :viewDidMoveToSuperview, :"movedFromWindow:", :"movedToWindow:", :"isDescendantOfView:", :window, :subviews, :setNeedsLayout, :superview, :removeFromSuperview, :"willRemoveSubview:", :layoutIfNeeded, :"addSubview:", :"sizeThatFits:", :"hitTest:withEvent:", :isMultipleTouchEnabled, :"resizeSubviewsWithOldSize:", :"pointInside:withEvent:", :"resizeWithOldSuperviewSize:", :autoresizingMask, :"setMultipleTouchEnabled:", :"setExclusiveTouch:", :isExclusiveTouch, :"convertSize:toView:", :"convertSize:fromView:", :hitRect, :"setFrame:forFields:", :"setRotationBy:", :"setAutoresizesSubviews:", :autoresizesSubviews, :sizeToFit, :origin, :"setOrigin:", :extent, :"setAutoresizingMask:", :"convertRect:toView:", :"convertRect:fromView:", :bounds, :transform, :position, :"setFrame:", :"setTransform:", :"setBounds:", :"convertPoint:fromView:", :"convertPoint:toView:", :frame, :center, :"setCenter:", :"setPosition:", :"setUserInteractionEnabled:", :charge, :"setTag:", :isUserInteractionEnabled, :cancelTouchTracking, :cancelMouseTracking, :tapDelegate, :"setCharge:", :"setTapDelegate:", :"startHeartbeat:inRunLoopMode:", :"stopHeartbeat:", :canHandleSwipes, :"swipe:withEvent:", :tag, :init, :layer, :nextResponder, :"initWithCoder:", :"encodeWithCoder:", :defaultFirstResponder, :nextFirstResponder, :inputAccessoryView, :inputView, :reloadInputViews, :reloadInputViewsWithoutReset, :"canPerformAction:withSender:",
:"touchesCancelled:withEvent:", :"motionEnded:withEvent:", :"touchesBegan:withEvent:", :"touchesMoved:withEvent:", :"touchesEnded:withEvent:", :"motionBegan:withEvent:", :"motionCancelled:withEvent:", :"remoteControlReceivedWithEvent:", :"mouseDragged:", :isFirstResponder, :canResignFirstResponder, :undoManager, :"mouseDown:", :"mouseExited:", :"mouseEntered:", :"scrollWheel:", :"mouseMoved:", :"mouseUp:", :firstResponder, :sessions, :help, :quit, :"repl:", :to_plist, :"Complex:", :Complex, :"Rational:", :Rational, :"enum_for:", :enum_for, :"to_enum:", :to_enum, :object_id, :__id__, :"define_singleton_method:", :define_singleton_method, :"public_method:", :"method:", :"extend:", :extend, :"respond_to_missing?:", :"respond_to?:", :respond_to?, :"public_send:", :public_send, :"send:", :send, :"__send__:", :__send__, :"instance_exec:", :instance_exec, :"instance_eval:", :instance_eval, :__callee__, :__method__, :tap, :"is_a?:", :"kind_of?:", :"instance_of?:", :"instance_variable_defined?:", :"instance_variable_set:", :"instance_variable_get:", :instance_variables, :"public_methods:", :public_methods, :"private_methods:", :private_methods, :"protected_methods:", :protected_methods, :"singleton_methods:", :singleton_methods, :"methods:", :methods, :inspect, :to_s, :untrusted?, :untrust, :trust, :frozen?, :freeze, :untaint, :tainted?, :taint, :__type__, :dup, :clone, :"<=>:", :"eql?:", :"!~:", :"=~:", :"===:", :nil?, :"!=:", :!, :"==:", :"equal?:", :selectionAffinity, :defaultAccessibilityTraits, :"accessibilitySetIdentification:", :"indexOfAccessibilityElement:", :"accessibilityElementAtIndex:", :accessibilityElementCount, :accessibilityIncrement, :accessibilityDecrement, :"accessibilityScroll:", :accessibilityPerformEscape, :accessibilityElementDidBecomeFocused, :accessibilityElementDidLoseFocus, :accessibilityElementIsFocused, :accessibilityFrame, :storedAccessibilityViewIsModal, :storedAccessibilityElementsHidden, :accessibilityHint, :accessibilityActivationPoint, :accessibilityTraits, :accessibilityLanguage, :accessibilityIdentifier, :accessibilityViewIsModal, :accessibilityElementsHidden, :storedAccessibilityTraits, :storedIsAccessibilityElement, :storedAccessibilityFrame, :storedAccessibilityActivationPoint, :"setAccessibilityElementsHidden:", :"setAccessibilityViewIsModal:", :"setAccessibilityHint:", :"setAccessibilityFrame:", :"setAccessibilityActivationPoint:", :"setAccessibilityTraits:", :"setIsAccessibilityElement:", :"setAccessibilityContainer:", :"setAccessibilityLanguage:", :"setAccessibilityIdentifier:", :accessibilityContainer, :accessibilityLabel, :"setAccessibilityValue:", :accessibilityValue, :"setAccessibilityLabel:", :isAccessibilityElement, :awakeFromNib, :classForPortCoder, :"replacementObjectForPortCoder:", :"methodDescriptionForSelector:", :"performSelectorOnMainThread:withObject:waitUntilDone:modes:", :"performSelector:onThread:withObject:waitUntilDone:modes:", :"performSelectorInBackground:withObject:", :"performSelector:onThread:withObject:waitUntilDone:", :"performSelectorOnMainThread:withObject:waitUntilDone:", :"performSelector:object:afterDelay:", :"performSelector:withObject:afterDelay:", :"performSelector:withObject:afterDelay:inModes:", :autoContentAccessingProxy, :classForCoder, :"replacementObjectForCoder:", :"awakeAfterUsingCoder:", :"implementsSelector:", :allowsWeakReference, :retainWeakReference, :classForKeyedArchiver, :"replacementObjectForKeyedArchiver:", :observationInfo, :"setObservationInfo:", :"willChange:valuesAtIndexes:forKey:", :"didChange:valuesAtIndexes:forKey:", :"willChangeValueForKey:withSetMutation:usingObjects:", :"didChangeValueForKey:withSetMutation:usingObjects:", :"didChangeValueForKey:", :"willChangeValueForKey:", :"removeObserver:forKeyPath:", :"addObserver:forKeyPath:options:context:", :"removeObserver:forKeyPath:context:", :"observeValueForKeyPath:ofObject:change:context:", :"setValuesForKeysWithDictionary:", :"dictionaryWithValuesForKeys:", :"setNilValueForKey:", :"validateValue:forKeyPath:error:", :"validateValue:forKey:error:", :"mutableArrayValueForKeyPath:",
:"mutableArrayValueForKey:", :"mutableOrderedSetValueForKeyPath:", :"mutableOrderedSetValueForKey:", :"mutableSetValueForKeyPath:", :"mutableSetValueForKey:", :"setValue:forKeyPath:", :"valueForKeyPath:", :"valueForUndefinedKey:", :"setValue:forUndefinedKey:", :"valueForKey:", :"replacementObjectForArchiver:", :classForArchiver, :isNSValue__, :isNSString__, :isNSNumber__, :isNSArray__, :isNSDictionary__, :isNSData__, :isNSDate__, :isNSSet__, :isNSOrderedSet__, :isNSTimeZone__, :"CA_interpolateValues:::interpolator:", :"CA_distanceToValue:", :"CA_interpolateValue:byFraction:", :"CA_addValue:multipliedBy:", :CA_copyRenderValue, :CA_prepareRenderValue, :pep_onMainThread, :"pep_onOperationQueue:priority:", :"pep_onThread:immediateForMatchingThread:", :"pep_onThread:", :"pep_getInvocation:", :"pep_onOperationQueue:", :"pep_afterDelay:", :pep_onDetachedThread, :pep_onMainThreadIfNecessary, :releaseOnMainThread, :class, :"isEqual:", :hash, :retain, :release, :autorelease, :retainCount, :copy, :finalize, :"forwardInvocation:", :"isKindOfClass:", :"respondsToSelector:", :"doesNotRecognizeSelector:", :"performSelector:", :"performSelector:withObject:", :self, :mutableCopy, :debugDescription, :"forwardingTargetForSelector:", :"methodSignatureForSelector:", :superclass, :zone, :"performSelector:withObject:withObject:", :isProxy, :"isMemberOfClass:", :"conformsToProtocol:", :isFault, :"methodForSelector:"] (main)>> self.methods.count 464
Thats so much information to handle to understand what these do , iOS API docs on Dash is a better option, but you get what is possible How do we identify the relevant methods
(main)>> def useful_methods(variable); variable.public_methods.select {|k| k == k.downcase} - " ".methods ; end
So useful_methods is too long change it into something memorable for you, I will define it as "um", I am typing again as alias method is not available as RubyMotion is still not a 100% ruby implementation
def um(variable); variable.public_methods.select {|k| k == k.downcase} - " ".methods ; end
(main)>> um(hello) ... (main)>> useful_methods(hello) => [:table, :keyboard, :subtitle, :dismiss, :buttons, :layout, :context, :show, :title, :delegate, :message, :alpha,
This is more manageable, what did that magic method do, since RubyMotion is using native Objective-C as primitive all the methods are visible and they follow camelCase and colon in methods. additionally i have removed common methods derived in any string instance. Let us hide the message from console
(main)>> hello.dismiss
Bring it back
(main)>> hello.show
We dont know what parameters are required but nothing big is lost, as the worst that might happen is the console crashes, but it is still a rare occasion. Whatever code works fine on console gets added to your editor and then gets commited to you repository and you are making progress one line at a time. Think about doing it on Xcode, each compile would take its time, and would be not the best way to develop quickly. You dont need to perfect code which would run on the first run, you just need to focus on the task in hand There are more than one way of doing anything in nearly every technology, but when you are learning learn one way of doing it first and go forward, over a period you would be a learnt man and can justify which is better as experience teaches you. WIP
10
Read Code
Reading code is the best way to learn programming. I have been daily searching Hacker News , github and RubyMotion group to learn for how RubyMotion works, discover projects, problems faced by other people. I have tried to automate the process of downloading all the new projects for you to discover on your mac. If you follow the following steps you can checkout over 40 repositories as of now, more would be added in coming days
It will take some time as about 100mb is downloaded directly from the repositories.
Read Code
11
See all the apps running in sequence, in your simulator, stopping app technique one and two might be occasionally required.
Read Code
12
Best Practices
WIP
Best Practices
13
Controllers
WIP
Controllers
14
Views
WIP
Views
15
TableView
WIP
TableView
16
17
Core Location
WIP
Core Location
18
Core Data
WIP
Core Data
19
Camera
WIP
Camera
20
Audio
WIP
NAME
video
Audio
21
Graphics
WIP
Graphics
22
Addressbook
WIP
Addressbook
23
Core Location
WIP
Core Location
24
25
Facebook
WIP
26
27
TestFlight
WIP
TestFlight
28
App Store
WIP
App Store
29