Upload
colin-gray
View
1.591
Download
1
Embed Size (px)
DESCRIPTION
This presentation is in two parts. The first part goes over how I refactored the Jukely app to have better separation of concerns by breaking up the controller into a "Storage" (aka Adapter, Repository) class and a "Layout" class. This technique worked very well for me, and resulted in cleaner code, more testable classes, and controllers that were easy to understand. I also present MotionKit, which is a gem that is designed to fulfill that "Layout" role. You can design and update your views very easily using MotionKit, and it can be used on iOS, OS X, and we already have Android support on the way! MotionKit was written by me and Jamon Holmgren.
Citation preview
Kill the Controllers
Kill the ControllersA terrible great way refactor large applications.
Mod
el Yes, there are models, fetched and created by the controller
View
Yes, views, too. The controller makes em!
Contro
ller
API Queries, UI, animations, network availability, login status, UI events,ticket purchasing, Facebook login, caching, scrolling updates, image downloading and processing,search filters, recommendations, notifications, sharing…
State
APIModels
Views
Layout
Storage
Controller
API
Models
Views
Controller
MVC?
MVC?❌
MQVLCS
StorageController
LayoutView
QueryModel
The benefits are many!
• Classes focus on one task
• Controllers don’t “do it all”
• Less code bloat all around
• Testable controllers
• Easier to add new features
• Easier to read and understand
– Colin T.A. Gray
“Don’t quote me on any of that.”
• Layout?
• Storage? WTF is that?
• Models & Queries?It’s your call.
Again, up to you.
There’s a gem for that. ;-)
credit: Gant Laborde
• Animations
• Events
• Styles
• Imagine a controller and a view got together and had an adorable love child
• Move your UI code out of your controller:
• Support for iOS and OS X
• Support for CALayer, NSMenu
• Frame and AutoLayout DSLs
• Add your own helpers just bydefining a class
class LoginLayout < MK::Layout # or < MK::WindowLayout # or < MK::MenuLayout view :username_field view :submit_button # @layout.submit_button
def layout background_color UIColor.blackColor
add UILabel, :login_label add UIView, :login_container do add UITextField, :username_field add UIButton, :submit_button end end
end
def login_label_style initial do # only apply when views are created text 'Login' backgroundColor UIColor.clearColor font UIFont.boldSystemFontOfSize(40) textAlignment UITextAlignmentCenter layer do shadowRadius 20 shadowOpacity 0.5 masksToBounds false end
constraints do width('100%') center_x.equals(:superview) end endend
# separate method to work with controller.topLayoutGuidedef add_constraints(controller) constraints(self.view) do origin [0, 0] width.equals(:superview) height.equals(:superview) end
constraints(:login_label) do below(controller.topLayoutGuide).plus(50) # define top_margin: 50 # below(controller.topLayoutGuide).plus(:top_margin) endend
MotionKit Extensions
def label_style frame from_top(:container, width: 200) frame below(:button) frame left_of(:other_label)
x 0 y 0 right 320 bottom '100% - 8'end
Frames
MotionKit ExtensionsConstraints
def label_style constraints do left.equals(:superview, :left).plus(8) right.equals(:button, :left).minus(8) top_left.equals(:superview) height.is <= 100 endend
MotionKit ExtensionsNSMenu
class MainMenu < MK::Menu def layout add 'File' do add 'Open', action: 'open:', key: 'o' add 'Close', action: 'close:', key: 'w' add 'Foo', :foo_menu add 'Bar', :bar_menu do add 'Baz' end end endend
MotionKit Extensionsdef label_style autoresizing_mask :pin_to_top layer do backgroundColor UIColor.blackColor.CGColor end
title 'Send' title 'Done', state: UIControlStateDisabled
portrait do frame [[0, 0], [100, 100]] end landscape do frame [[20, 0], [140, 100]] endend
shadow_color UIColor.colorWithRed(0.133, green: 0.133, blue: 0.133)text_color UIColor.colorWithRed(0.6, green: 0.6, blue: 0.6)
font UIFont.fontWithName(‘Avenir-Medium’, size: 20)
line_break_mode UILineBreakModeWordWraptext_alignment NSTextAlignmentCenterbaseline_adjustment UIBaselineAdjustmentNone
SweetKitUses SugarCube methods to turn this…
shadow_color '#222222'text_color '#999999'
font 'Avenir-Medium', size: 20
line_break_mode :word_wraptext_alignment :centerbaseline_adjustment :none
SweetKit…into this!
MotionKit::EventsDecouple your UI events
class LoginLayout < MK::Layout def login_button_style target.on :touch do trigger(:login, username_field.text.to_s, @password_field.text.to_s) end endend
class LoginController < UIViewController def viewDidLoad super @layout = LoginLayout.new(root: self.view)
@layout.on :login do |username, password| handle_login(username, password) end endend
Roadmap
• View Layout helpers
• Spec helpers
• Layouts (e.g. Linear flow)
• Tutorials
• Screencasts
• Documentation
– Colin T.A. Gray
“Oh yeah, Jamon I forgot to tell you.”
class LoginLayout < MK::Layout view :username_field view :submit_button
def layout add Android::Widget::LinearLayout, :login_container do orientation VERTICAL
add Android::View::TextView, :username_field add Android::View::Button, :submit_button end end
def submit_button_style text 'Login' end
end
MotionKit on Android
Kill the Controllers!
• Controllers should do the minimum amount of “paper pushing”
• UI and animations → Layout
• Talking to the API → Storage
• etc. (Misc Task → Misc Class)
DEMOExample of a LoginController written with
Storage and Layout classes.
You can see some similar code here:github.com/rubymotion/motion-kit-events
Colin T.A. GrayCommunity Manager at HipByte
Engineer at Jukelygithub.com/colinta
github.com/rubymotion/motion-kitgem install motion-kit
github.com/colinta/motion-xraygem install motion-xray