From 22a34f97e3924460a226dacf9cc1343a8989739d Mon Sep 17 00:00:00 2001 From: Mark Rickert Date: Fri, 20 Feb 2015 11:42:06 -0600 Subject: [PATCH 1/4] Adds auto_set_content_size for UIScrollview and subclasses. So that you can add a bunch of things toa scrollview and then call auto_set_content_size and it will not change frame but automatically set the contentSize based on the scroll view's contents (with optional padding) --- motion/ruby_motion_query/position.rb | 24 ++++++++++++++++++++++++ spec/position.rb | 28 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/motion/ruby_motion_query/position.rb b/motion/ruby_motion_query/position.rb index 6c77d2f..0117a40 100644 --- a/motion/ruby_motion_query/position.rb +++ b/motion/ruby_motion_query/position.rb @@ -127,6 +127,30 @@ module RubyMotionQuery self end + def auto_set_content_size(padding = {}) + selected.each do |view| + unless view.respond_to?(:contentSize) + puts "\n[RMQ ERROR] auto_set_content_size must be called on a view that supports `contentSize`. Called on #{view}\n\n" + next + end + + w = 0 + h = 0 + + view.subviews.each do |subview| + rect = subview.rmq.frame + w = [rect.right, w].max + h = [rect.bottom, h].max + end + + view.rmq.style do |st| + st.content_size = CGSizeMake(w + (padding[:right] || 0), h + (padding[:bottom] || 0)) + end + end + + self + end + # @return [Array] or [CGSize] def location_in_root_view self.location_in(self.root_view) diff --git a/spec/position.rb b/spec/position.rb index 47f8ab3..e6f5927 100644 --- a/spec/position.rb +++ b/spec/position.rb @@ -118,6 +118,34 @@ describe 'position' do view.size.height.should == 500 end + it "should set a UIScrollView's contentSize property automatically" do + view = @vc.rmq.append(UIScrollView).layout(h: 100, w: 20).get + view.rmq.append(UIButton).layout(h: 50, w: 10) + view.rmq.append(UILabel).layout(h: 500, w: 70) + view.rmq.append(UIView).layout(h: 5, w: 1) + + view.contentSize.should == CGSizeZero + + view.rmq.auto_set_content_size + view.contentSize.should == CGSizeMake(70, 500) + + # Right padding + view.rmq.auto_set_content_size({right: 20}) + view.contentSize.should == CGSizeMake(90, 500) + + # Bottom padding + view.rmq.auto_set_content_size({bottom: 21}) + view.contentSize.should == CGSizeMake(70, 521) + + # Right and bottom padding + view.rmq.auto_set_content_size({right: 19, bottom: 2}) + view.contentSize.should == CGSizeMake(89, 502) + + # Negative padding + view.rmq.auto_set_content_size({right: -2, bottom: -4}) + view.contentSize.should == CGSizeMake(68, 496) + end + it 'should nudge a view in various directions' do view = @vc.rmq.append(UILabel).get view.origin.x.should == 0 From b1817f93708e2b7b8bd497637386f3191f71c23d Mon Sep 17 00:00:00 2001 From: Mark Rickert Date: Fri, 20 Feb 2015 11:58:39 -0600 Subject: [PATCH 2/4] DRY --- motion/ruby_motion_query/position.rb | 49 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/motion/ruby_motion_query/position.rb b/motion/ruby_motion_query/position.rb index b512bcf..db76c80 100644 --- a/motion/ruby_motion_query/position.rb +++ b/motion/ruby_motion_query/position.rb @@ -108,20 +108,7 @@ module RubyMotionQuery def resize_to_fit_subviews(padding = {}) selected.each do |view| - w = 0 - h = 0 - - view.subviews.each do |subview| - rect = subview.rmq.frame - w = [rect.right, w].max - h = [rect.bottom, h].max - end - - rect = view.rmq.frame - w = rect.width if w == 0 - h = rect.height if h == 0 - - view.rmq.layout(w: (w + (padding[:right] || 0)), h: (h + (padding[:bottom] || 0))) + view.rmq.layout(subviews_bottom_right(view, padding)) end self @@ -134,17 +121,9 @@ module RubyMotionQuery next end - w = 0 - h = 0 - - view.subviews.each do |subview| - rect = subview.rmq.frame - w = [rect.right, w].max - h = [rect.bottom, h].max - end - view.rmq.style do |st| - st.content_size = CGSizeMake(w + (padding[:right] || 0), h + (padding[:bottom] || 0)) + bottom_right = subviews_bottom_right(view, padding) + st.content_size = CGSizeMake(bottom_right[:w], bottom_right[:h]) end end @@ -166,5 +145,27 @@ module RubyMotionQuery out end + private + + # Calculates the bottom right of a view using its subviews + # + # @return [Hash] + def subviews_bottom_right(view, padding = {}) + w = 0 + h = 0 + + view.subviews.each do |subview| + rect = subview.rmq.frame + w = [rect.right, w].max + h = [rect.bottom, h].max + end + + rect = view.rmq.frame + w = rect.width if w == 0 + h = rect.height if h == 0 + + {w: (w + (padding[:right] || 0)), h: (h + (padding[:bottom] || 0))} + end + end end From 00dc9468845bb08401676e9592d8d803a2e073ae Mon Sep 17 00:00:00 2001 From: Mark Rickert Date: Mon, 23 Feb 2015 13:47:44 -0600 Subject: [PATCH 3/4] Rename resize_content_to_fit_subviews and resize_frame_to_fit_subviews To fit conventions agreed upon. --- motion/ruby_motion_query/position.rb | 7 ++++--- spec/position.rb | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/motion/ruby_motion_query/position.rb b/motion/ruby_motion_query/position.rb index db76c80..c8f52cc 100644 --- a/motion/ruby_motion_query/position.rb +++ b/motion/ruby_motion_query/position.rb @@ -106,18 +106,19 @@ module RubyMotionQuery self end - def resize_to_fit_subviews(padding = {}) + def resize_frame_to_fit_subviews(padding = {}) selected.each do |view| view.rmq.layout(subviews_bottom_right(view, padding)) end self end + alias :resize_to_fit_subviews :resize_frame_to_fit_subviews - def auto_set_content_size(padding = {}) + def resize_content_to_fit_subviews(padding = {}) selected.each do |view| unless view.respond_to?(:contentSize) - puts "\n[RMQ ERROR] auto_set_content_size must be called on a view that supports `contentSize`. Called on #{view}\n\n" + puts "\n[RMQ ERROR] resize_content_to_fit_subviews must be called on a view that supports `contentSize`. Called on #{view}\n\n" next end diff --git a/spec/position.rb b/spec/position.rb index f61f13e..455e4a7 100644 --- a/spec/position.rb +++ b/spec/position.rb @@ -101,7 +101,7 @@ describe 'position' do view.rmq.append(UIButton).layout(h: 50, w: 10) view.rmq.append(UIButton).layout(h: 5, w: 1) view.size.width.should == 20 - view.rmq.resize_to_fit_subviews + view.rmq.resize_frame_to_fit_subviews view.size.width.should == 10 view.size.height.should == 50 end @@ -113,7 +113,7 @@ describe 'position' do view.rmq.append(UIView).layout(h: 5, w: 1) view.size.width.should == 20 view.size.height.should == 100 - view.rmq.resize_to_fit_subviews + view.rmq.resize_frame_to_fit_subviews view.size.width.should == 70 view.size.height.should == 500 end @@ -127,22 +127,22 @@ describe 'position' do view.size.height.should == 100 # Just right - view.rmq.resize_to_fit_subviews({right: 101}) + view.rmq.resize_frame_to_fit_subviews({right: 101}) view.size.width.should == 171 view.size.height.should == 500 # Just bottom - view.rmq.resize_to_fit_subviews({bottom: 13}) + view.rmq.resize_frame_to_fit_subviews({bottom: 13}) view.size.width.should == 70 view.size.height.should == 513 # Both right & bottom - view.rmq.resize_to_fit_subviews({right: 101, bottom: 13}) + view.rmq.resize_frame_to_fit_subviews({right: 101, bottom: 13}) view.size.width.should == 171 view.size.height.should == 513 # Negative values - view.rmq.resize_to_fit_subviews({right: -1, bottom: -1}) + view.rmq.resize_frame_to_fit_subviews({right: -1, bottom: -1}) view.size.width.should == 69 view.size.height.should == 499 end @@ -155,23 +155,23 @@ describe 'position' do view.contentSize.should == CGSizeZero - view.rmq.auto_set_content_size + view.rmq.resize_content_to_fit_subviews view.contentSize.should == CGSizeMake(70, 500) # Right padding - view.rmq.auto_set_content_size({right: 20}) + view.rmq.resize_content_to_fit_subviews({right: 20}) view.contentSize.should == CGSizeMake(90, 500) # Bottom padding - view.rmq.auto_set_content_size({bottom: 21}) + view.rmq.resize_content_to_fit_subviews({bottom: 21}) view.contentSize.should == CGSizeMake(70, 521) # Right and bottom padding - view.rmq.auto_set_content_size({right: 19, bottom: 2}) + view.rmq.resize_content_to_fit_subviews({right: 19, bottom: 2}) view.contentSize.should == CGSizeMake(89, 502) # Negative padding - view.rmq.auto_set_content_size({right: -2, bottom: -4}) + view.rmq.resize_content_to_fit_subviews({right: -2, bottom: -4}) view.contentSize.should == CGSizeMake(68, 496) end From 1179114dd24c21217ed3ab16a74af2183ea46f26 Mon Sep 17 00:00:00 2001 From: Mark Rickert Date: Wed, 25 Feb 2015 20:03:51 -0600 Subject: [PATCH 4/4] New feature for resize_frame_to_fit_subviews Allows you to pass :only_width and :only_height so that things aren't resized that you don't want to be :) --- motion/ruby_motion_query/position.rb | 16 +++---- spec/position.rb | 66 ++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/motion/ruby_motion_query/position.rb b/motion/ruby_motion_query/position.rb index c8f52cc..affddb7 100644 --- a/motion/ruby_motion_query/position.rb +++ b/motion/ruby_motion_query/position.rb @@ -106,16 +106,16 @@ module RubyMotionQuery self end - def resize_frame_to_fit_subviews(padding = {}) + def resize_frame_to_fit_subviews(args = {}) selected.each do |view| - view.rmq.layout(subviews_bottom_right(view, padding)) + view.rmq.layout(subviews_bottom_right(view, args)) end self end alias :resize_to_fit_subviews :resize_frame_to_fit_subviews - def resize_content_to_fit_subviews(padding = {}) + def resize_content_to_fit_subviews(args = {}) selected.each do |view| unless view.respond_to?(:contentSize) puts "\n[RMQ ERROR] resize_content_to_fit_subviews must be called on a view that supports `contentSize`. Called on #{view}\n\n" @@ -123,7 +123,7 @@ module RubyMotionQuery end view.rmq.style do |st| - bottom_right = subviews_bottom_right(view, padding) + bottom_right = subviews_bottom_right(view, args) st.content_size = CGSizeMake(bottom_right[:w], bottom_right[:h]) end end @@ -151,7 +151,7 @@ module RubyMotionQuery # Calculates the bottom right of a view using its subviews # # @return [Hash] - def subviews_bottom_right(view, padding = {}) + def subviews_bottom_right(view, args = {}) w = 0 h = 0 @@ -162,10 +162,10 @@ module RubyMotionQuery end rect = view.rmq.frame - w = rect.width if w == 0 - h = rect.height if h == 0 + w = rect.width if (w == 0 || args[:only_height]) + h = rect.height if (h == 0 || args[:only_width]) - {w: (w + (padding[:right] || 0)), h: (h + (padding[:bottom] || 0))} + {w: (w + (args[:right] || 0)), h: (h + (args[:bottom] || 0))} end end diff --git a/spec/position.rb b/spec/position.rb index 455e4a7..d762813 100644 --- a/spec/position.rb +++ b/spec/position.rb @@ -127,26 +127,92 @@ describe 'position' do view.size.height.should == 100 # Just right + view.rmq.layout(h:100, w:20) view.rmq.resize_frame_to_fit_subviews({right: 101}) view.size.width.should == 171 view.size.height.should == 500 # Just bottom + view.rmq.layout(h:100, w:20) view.rmq.resize_frame_to_fit_subviews({bottom: 13}) view.size.width.should == 70 view.size.height.should == 513 # Both right & bottom + view.rmq.layout(h:100, w:20) view.rmq.resize_frame_to_fit_subviews({right: 101, bottom: 13}) view.size.width.should == 171 view.size.height.should == 513 # Negative values + view.rmq.layout(h:100, w:20) view.rmq.resize_frame_to_fit_subviews({right: -1, bottom: -1}) view.size.width.should == 69 view.size.height.should == 499 end + it "should resize a view's frame without changing height if specified" do + view = @vc.rmq.append(UIView).layout(h: 100, w: 20).get + view.rmq.append(UIButton).layout(h: 50, w: 10) + view.rmq.append(UILabel).layout(h: 500, w: 70) + view.rmq.append(UIView).layout(h: 5, w: 1) + view.size.width.should == 20 + view.size.height.should == 100 + + view.rmq.resize_frame_to_fit_subviews({only_height: true}) + view.size.width.should == 20 + view.size.height.should == 500 + + # With bottom padding + view.rmq.layout(h:100, w:20) + view.rmq.resize_frame_to_fit_subviews({only_height: true, bottom: 10}) + view.size.width.should == 20 + view.size.height.should == 510 + + # With right padding + view.rmq.layout(h:100, w:20) + view.rmq.resize_frame_to_fit_subviews({only_height: true, right: 10}) + view.size.width.should == 30 + view.size.height.should == 500 + + # With both padding + view.rmq.layout(h:100, w:20) + view.rmq.resize_frame_to_fit_subviews({only_height: true, bottom:21, right: 10}) + view.size.width.should == 30 + view.size.height.should == 521 + end + + it "should resize a view's frame without changing width if specified" do + view = @vc.rmq.append(UIView).layout(h: 100, w: 20).get + view.rmq.append(UIButton).layout(h: 50, w: 10) + view.rmq.append(UILabel).layout(h: 500, w: 70) + view.rmq.append(UIView).layout(h: 5, w: 1) + view.size.width.should == 20 + view.size.height.should == 100 + + view.rmq.resize_frame_to_fit_subviews({only_width: true}) + view.size.width.should == 70 + view.size.height.should == 100 + + # With bottom padding + view.rmq.layout(h:100, w:20) + view.rmq.resize_frame_to_fit_subviews({only_width: true, bottom: 10}) + view.size.width.should == 70 + view.size.height.should == 110 + + # With right padding + view.rmq.layout(h:100, w:20) + view.rmq.resize_frame_to_fit_subviews({only_width: true, right: 10}) + view.size.width.should == 80 + view.size.height.should == 100 + + # With both padding + view.rmq.layout(h:100, w:20) + view.rmq.resize_frame_to_fit_subviews({only_width: true, bottom:21, right: 10}) + view.size.width.should == 80 + view.size.height.should == 121 + end + it "should set a UIScrollView's contentSize property automatically" do view = @vc.rmq.append(UIScrollView).layout(h: 100, w: 20).get view.rmq.append(UIButton).layout(h: 50, w: 10)