Comments (37)
I've been an iOS dev since 4.0, so I might be a little stuck in my ways, but personally I'm fine with the way things are working currently. The only thing that would make me even happier would be if the add()
method would also be defined on UIView
so that I could do things like:
class WeatherView < UIView
attr_accessor :conditions
def self.new(conditions=nil)
wv = alloc.initWithFrame([[0,0], [320,480]])
wv.conditions = conditions
wv
end
def initWithFrame(f)
super
add UILabel, font: :bold.uifont(12), backgroundColor: :clear.uicolor, placeholder: "Conditions"
self
end
end
This would also allow me to use add()
more robustly in my controller, since I could call it on self and add to the self.view
, but then I could also add items to existing views easily too:
class HomeScreen < PM::Screen
def view_did_load
@box = add UIView, frame: [[10,10],[40,40]], backgroundColor: :red.uicolor
@box.add UILabel, font: :bold.uifont(12), backgroundColor: :clear.uicolor, placeholder: "Conditions"
end
end
My rule of thumb is if I add more than 3-4 views in my controller, I need to create a view subclass & do my setup there. But it's so much easier to use the hash attributes syntax that add()
provides :)
from promotion.
Pixate looks awesome and seems to be best equipped for out-of-the-box good design (e.g. with their Bootstrap port).
from promotion.
Let's move add
into its own module and make it work with UIView subclasses, then. Work for you?
from promotion.
I agree, Alan. Pixate is definitely cool -- I was one of the Kickstarter supporters and have a T-shirt even. Haha...
from promotion.
Yah, Pixate is really cool. But I'm sure a lot of people don't want to pay for the license.
When I first checked out ProMotion this week (after working on many ios apps from first release of the sdk), I thought, yah, I like this approach of making things less obj-c (and I'm prob one of the few people who doesn't mind obj-c). Right now at my office we've fallen into the Storyboard trap - namely issues with merges all the time and not even sure you touched something which shows up as a regression in the build.
I've been thinking a lot about this problem for a few months and while we won't be switching our project over to Ruby Motion anytime soon, I still have a good sense of what I would like (in an ideal world). Maybe something between css and teacup..? (below doesn't really share much with css except in how properties could be considered for inclusion)
Stylescreen.new(:home) do
# this could live in a base style class
view do
backgroundColor: "#edebe6".uicolor
end
# as could this
button do
titleFont: fontWithName('Signika-Regular', size:20),
backgroundColor: "#edebe6".uicolor,
borderWidth: 1.0,
borderColor: "#e0ded9".uicolor,
cornerRadius: 5.0
end
submit_button :extends => :button, :instance => true do
left: 10,
right: 10,
bottom: constrain_above(:cancel_button)
end
cancel_button :extends => :button do
left: 10,
right: 10,
bottom: constrain_pin(:bottom)
end
end
class HomeScreen < PM::Screen
# Not even sure this would be needed.. or maybe HomeScreen < PM::StyledScreen
style :home
def on_load
# So we could hook up the events later
@cancel_button = from_style(:cancel_button)
@submit_button = from_style(:submit_button)
add [@cancel_button, @submit_button]
# or maybe by setting the :instance => true in the style you get a free @submit_button and
# you can avoid the above add to view code..? Just a thought. There's definitely reasons
# one might not want to do this, but this is ruby and i want to type less and get more :)
end
end
Obviously this is closely related to teacup, but to me, this feels much more of what I would want with a Ruby library. It also gives me the added power of defining what I need in the context of ProMotion but not having to try to hack in teacup. I would love to ditch the CGRectMake calls in my implementation classes and have it work in the style definitions with autolayout. (Autolayout support is a must have in my mind.)
It's probably a decent amount of work with constraints, the implicit alloc/inits, and needing to decide what styles can be applied to the views/controls and layers. Probably less work than creating something like pixate tho..
Anyway, I just thought I'd chime in and it could be something I could set aside some time to contribute.
from promotion.
Thanks for the input, Jason! Good stuff.
One thing I want is to have a common handler for conversion of camel case to underscores. We can do that with a monkeypatch of String like the ActiveSupport's Inflector class:
class String
def to_snakecase
self.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
def to_camelcase(uppercase_first_letter=true)
string = self.dup
if uppercase_first_letter
string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
else
string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
end
string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
end
end
This would allow us to handle ruby-style snake case and Obj-C's headless camel case for these attributes.
from promotion.
If you haven't caught on by now, nothing's really sacred with me as far as monkeypatching the crap out of RubyMotion. As long as it doesn't cause bugs or make a lot of gems incompatible I take a pretty pragmatic approach to this.
from promotion.
Just thinking out loud (figuratively) here...
ProMotion::Styles.define do
style login_button: {
title_font: fontWithName('Signika-Regular', size:20),
background_color: "#edebe6".uicolor,
border_width: 1.0,
border_color: "#e0ded9".uicolor,
corner_radius: 5.0
}
style profile_pic_view: {
custom_style: something,
layer: {
something_inside_layer: 5
}
}
end
class HomeScreen < ProMotion::Screen
def will_appear
add UIButton, style: :login_button, on_touch: :log_in_action
add CustomProfileView, style: :profile_pic_view, on_touch: :maximize_profile_action
end
# ...
end
Make add
have all of the proper initializers mapped to as many UIKit objects as we can think of so you can just pass in the class and get a blank instance out of it.
Maybe the on_touch
is going too far.
It might work better to make the style hashes into lambda blocks and not run them until called. I wouldn't want all that stuff sitting in memory.
from promotion.
Jamon,
I like that! Seems like a good direction to start with. I think you're right regarding the lambda blocks too. My one question would be what would make the most sense for breaking up the styles and then including them as needed. Not a hard problem to solve, but it would probable make sense from a maintenance perspective.
from promotion.
Jamon,
Your last proposition is very nice and I like the approch and the on_touch
possibility. It could be great to have the possibilty to define the style inside the class of the view or the controller to avoid multiple class definition in addition to this (and be more readable).
And you with that kind of code, you could style an element and have a another element in an other view or controller with the same "style class" and be different.
class HomeScreen < ProMotion::Screen
def will_appear
add UIButton, style: :login_button, on_touch: :log_in_action
end
def styling
style login_button: {
title_font: fontWithName('Signika-Regular', size:20),
background_color: "#edebe6".uicolor,
border_width: 1.0,
border_color: "#e0ded9".uicolor,
corner_radius: 5.0
}
end
end
from promotion.
Good point, @Swatto ! That would actually work really well. Although, is that really much different than defining a method that returns the styles?
class HomeScreen < ProMotion::Screen
def will_appear
add UIButton, style: login_button_style, on_touch: :log_in_action
end
def login_button_style
{
title_font: fontWithName('Signika-Regular', size:20),
background_color: "#edebe6".uicolor,
border_width: 1.0,
border_color: "#e0ded9".uicolor,
corner_radius: 5.0
}
end
end
from promotion.
I think this approch is less powerfull than the styling
method :
The def styling
with all of the definition in it, have a major feature : we can generate style with event (a return name from a server, from an input of the user, etc).
With the name of the styling in a method definition name, we can't generate style by his name.
What do you think about it ?
from promotion.
You could do:
method_name_here = :login_button_style
add UIButton, style: send(method_name_here), on_touch: :log_in_action
send
calls a method by a symbol or string name.
I do think this needs careful thought. We don't want to overcomplicate it.
The thing that I'm considering right now is lazy-loading the attributes hash. We don't want to hold all the attribute hashes in memory if they're not in use. A lambda or block would work but would be a bit awkward in all the scenarios I've imagined. Any thoughts?
from promotion.
I agree with Swatto, but not only for reasons listed, but also because of code separation and sharing.
Putting styles in a method of the (for instance) HomeScreen takes away all the power of this concept. Like css, a big part of the power of styles (for me anyway) is reuse and extensibility. I want one base button, for example, and then extend off that for multiple screens. And having this adhere to a file/naming convention, outside of implementation, would be really cool.
from promotion.
Agree, Jason. I'll do some thinking on this next week. Ideas welcome.
from promotion.
The thing that has me stuck on this one still is the memory usage. I don't want to store all the styles (and their associated objects) in memory in the chance that an element might use it. Even Teacup ends up instantiating a lot of styles without using them if you re-use stylesheets across ViewControllers.
It likely makes the most sense to create procs that only run when that style is called.
ProMotion::Styles.define do
style :login_button do {
background_color: UIColor.blackColor,
font: fontWithName('Signika-Regular', size:20)
} end
style :logout_button, inherit: [ :login_button, :base_styles ] do {
background_color: UIColor.redColor,
font: fontWithName('Signika-Bold', size:20)
} end
# or maybe...
style :logout_button do {
include: [ :login_button, :base_styles ],
background_color: UIColor.redColor,
font: fontWithName('Signika-Bold', size:20)
} end
end
The syntax looks slightly wonky, I realize, with the embedded hash, but this would be the most performant way to do this I believe -- a block (or proc) with a hash inside.
The inherit
thing seems like it would work pretty well. You just merge the hashes. This would be fairly straightforward to implement.
from promotion.
Check out: https://github.com/tombenner/nui
And see an implementation i did here: https://github.com/Skookum/RubyMotionTalk/tree/master/RedditRss
I don't think element styling should be a part of ProMotion
when there are so many other libraries that do it so well.
from promotion.
That looks really good. On your RedditRss app, I didn't see your stylesheet -- did I miss it somewhere?
from promotion.
I think I'll write an entire Styling section in the README and talk about Teacup, NUI, Pixate, and of course the built-in ProMotion system. I'm not sure how far I'll take the built-in system (it's fairly good already for in-place styling) -- the above is an idea and really depends on whether devs would like something like that or if they already have a favorite styling system.
I think a mixture of systems would work fine too -- we don't have to only support one. Teacup 2.0 (https://github.com/colinta/teacup/tree/2.0) looks like it will be pretty good and include support for Pixate and NUI. So it probably makes sense to just incrementally improve the internal system and recommend some of the other systems for more complex needs.
from promotion.
The stylesheet i'm using is the NUI default, therefore no need to declare it :)
One neat thing about NUI is that you can call setAutoUpdatePath
with an absolute path for testing and as you save the stylesheet file, the interface in the simulator updates automatically! https://github.com/Skookum/RubyMotionTalk/blob/master/RedditRss/app/app_delegate.rb#L3
from promotion.
My only beef with NUI is that it's iOS 6 only... but when iOS 7 comes, out, I'll be dropping support for iOS 5.
iOS 5.1.1 is currently about 3% of my user base (roughly 10k people). Everyone else is on some versions of 6.
from promotion.
Yah, the NUI autoupdate is pretty cool, forgot abou that. :)
I have to admit tho, I do like Jamon's direction. Not that I couldn't do it in NUI or teacup, but this
style :logout_button do {
include: [ :login_button, :base_styles ],
background_color: UIColor.redColor,
font: fontWithName('Signika-Bold', size:20)
} end
just looks very nice and clean to me. And that's the reason I gravitated towards ProMotion - I like the abstraction and the way things are represented. I dunno, the syntax just speaks to me more than the others.
@jamon, you're probably right about the instantiation - would be interesting to see what the memory usage might be for curiosity's sake.
from promotion.
I think if it doesn't take too much to implement (which I don't think it will) we might do this. Just gives users another option. But I'll make it clear that ProMotion can be used with your favorite styling system.
It'll be in a later release. Perhaps 1.0.
from promotion.
Damn. Promotion is going to eclipse bubblewrap in terms of functionality soon
from promotion.
@markrickert Touché ...
I'm certainly willing to be talked out of this. I don't want to fall into the "not built here" syndrome. I agree with Jason that this syntax would make a lot of sense to me, though.
from promotion.
it seems as though you guys have accidentally added my account to this
thread ([email protected]).
On Wed, May 8, 2013 at 11:29 AM, Jamon Holmgren [email protected]:
@markrickert https://github.com/markrickert Touché ...
I'm certainly willing to be talked out of this. I don't want to fall into
the "not built here" syndrome. I agree with Jason that this syntax would
make a lot of sense to me, though.—
Reply to this email directly or view it on GitHubhttps://github.com//issues/46#issuecomment-17617388
.
from promotion.
With a name as cool as that, @jamon, you should be involved. :) Sorry about that. Feel free to unwatch, of course.
from promotion.
Oh, whoops, sorry. I should have paid more attention.
@jamonholmgren I guess it's up to you, and sure, we don't want to fall into the not built here thing, but like I was saying earlier, the approach your taking feels best to me. So I guess that's my two cents.
from promotion.
I really like this, to throw my 2c in. Also a fan of the on_touch event.. :)
from promotion.
I'm currently working on a way to integrate the parts of Teacup that can help us here.
from promotion.
Looking at Teacup in more detail, I think it will work fine as-is. Since Screens are UIViewControllers, you can use Teacup normally.
I would recommend you use the instance method syntax (rather than the class method syntax):
class StyleScreen < PM::Screen
title "Styles"
stylesheet :standard
def will_appear
@view_setup ||= begin
layout(self.view, :root) do
# these subviews will be added to `self.view`
subview(UIToolbar, :toolbar)
subview(UIButton, :hi_button)
end
true
end
end
end
I'll write up a styling with Teacup guide at some point and include it in the repo.
from promotion.
So does this mean PM will only use Teacup for styling or will it have its own styling as well?
from promotion.
@Jasonbit: ProMotion will continue to have its own lightweight styling system with set_attributes
and add
. I'll continue to develop that as well and may even incorporate some of the ideas we had here. But after looking into what they've done with Teacup I think it's a fine choice for more demanding styling requirements.
from promotion.
ProMotion now supports Teacup styling in a very simple way. Just add stylesheet
to your screen and stylename:
to your add
or set_attributes
calls.
class HomeScreen < PM::Screen
stylesheet :home
def will_appear
add UILabel.new, stylename: :teacup_style
end
end
from promotion.
I decided to write a guide to styling views. It uses the simplest solution; one that doesn't eat memory nor require any modification to the current ProMotion styling code.
https://github.com/clearsightstudio/ProMotion/wiki/Guide%3A-Styling-Your-Views
Essentially, you define a module that gets mixed into your screen. To extend, it's relatively easy using parentstyle.merge({ new_styles: :whatever})
. Just ruby, no magic. Then, you use that method in your add element call.
module MyStyles
def button_style
{
background_color: UIColor.whiteColor
}
end
end
class MyScreen < PM::Screen
include MyStyles
def on_load
add some_button, button_style
add some_other_button, button_style.merge({
some_override: :value
})
end
end
from promotion.
Has the add UILabel.new, stylename: :foo
for Teacup option disappeared? :)
It doesn't work for me, and I can't locate it in the source files anywhere.
from promotion.
Addressed your question in the PR.
from promotion.
Related Issues (20)
- Unable to position a TableView by using frame style HOT 4
- ProMotion lifecycle methods running out of order HOT 1
- Unable to add remote image to nav_bar button HOT 3
- Switching to Screen if already open HOT 2
- How to scroll TableScreen to bottom programatically? HOT 2
- Change tab_bar backgroundcolor HOT 2
- Remote images not showing on first load of Table Screen HOT 3
- use custom class for open_tab_bar HOT 1
- Image picker crashes in iOS10 HOT 1
- 'live' and 'pm_live' don't work anymore with version 4.13 HOT 10
- app delegate methods never called HOT 2
- Error can't convert CommentScreen into String (TypeError) HOT 5
- CollectionScreen crashes after reloading data HOT 1
- Cynical dependencies HOT 3
- Circular dependencies on High Sierra HOT 11
- search_text no longer works
- uninitialized constant ProMotion::UITabBarController (NameError) HOT 6
- Regression: WebScreen synchronous javascript execution
- Regression (or simply failing test): table screen `on_reuse` is not called after scrolling
- Ruby 2.6 deprecations removed in Ruby 2.7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from promotion.