lib/motion/layout.rb in rm-extensions-0.1.8 vs lib/motion/layout.rb in rm-extensions-0.1.9

- old
+ new

@@ -39,21 +39,55 @@ AXIS_LOOKUP = { "h" => UILayoutConstraintAxisHorizontal, "v" => UILayoutConstraintAxisVertical } + # keeps track of views that are not #hidden? as constraints are built, so the + # special `last_visible` view name can be used in equations. + # exposed for advanced layout needs. + attr_accessor :visible_items + + # Example: + # RMExtensions::Layout.new do |layout| + # ... + # end def initialize @visible_items = [] + @constraints = {} if block_given? yield self end end + # reopens the RMExtensions::Layout instance for additional processing, ex: + # @layout.reopen do |layout| + # ... + # end + # note: you would need to store your instance somewhere on creation to be able to reopen it later, ex: + # @layout = RMExtensions::Layout.new do |layout| + # ... + # end + def reopen + if block_given? + yield self + end + self + end + def clear! @view.removeConstraints(@view.constraints) end + def remove(constraint) + constraints = [ constraint ].flatten + @view.removeConstraints(constraints) + @constraints.keys.each do |key| + @constraints.delete(key) if constraints.include?(@constraints.fetch(key)) + end + true + end + def view(view) @view = view end def view=(v) @@ -71,18 +105,21 @@ def subviews=(views) subviews(views) end + # takes a string one or more equations separated by newlines and + # processes each. returns an array of constraints def eqs(str) str.split("\n").map(&:strip).select { |x| !x.empty? }.map do |line| eq(line) - end + end.compact end # Constraints are of the form "view1.attr1 <relation> view2.attr2 * multiplier + constant @ priority" - def eq(str) + # processes one equation string + def eq(str, remove=false) parts = str.split("#", 2).first.split(" ").select { |x| !x.empty? } return if parts.empty? item = nil item_attribute = nil @@ -189,10 +226,12 @@ errors.push("Invalid attr1: #{item_attribute}") unless res_item_attribute errors.push("Invalid relatedBy: #{related_by}") unless res_related_by errors.push("Invalid view2: #{to_item}") if to_item && !res_to_item errors.push("Invalid attr2: #{to_item_attribute}") unless res_to_item_attribute + internal_ident = "#{item}.#{item_attribute} #{related_by} #{to_item}.#{to_item_attribute} * #{multiplier} @ #{priority}" + if errors.size > 0 || debug p "======================== constraint debug ========================" p "given:" p " #{str}" p "interpreted:" @@ -202,39 +241,71 @@ p " to_item: #{to_item}" p " to_item_attribute: #{to_item_attribute}" p " multiplier: #{multiplier}" p " constant: #{constant}" p " priority: #{priority || "required"}" + p " internal_ident: #{internal_ident}" end if errors.size > 0 raise(errors.join(", ")) end unless res_item.hidden? @visible_items.unshift(item) end - constraint = NSLayoutConstraint.constraintWithItem(res_item, - attribute:res_item_attribute, - relatedBy:res_related_by, - toItem:res_to_item, - attribute:res_to_item_attribute, - multiplier:res_multiplier, - constant:res_constant) - if res_priority - constraint.priority = res_priority + if remove + if constraint = @constraints[internal_ident] + if debug + p "status:" + p " existing (for removal)" + end + @view.removeConstraint(constraint) + else + raise "RMExtensions::Layout could not find constraint to remove for internal_ident: `#{internal_ident}` (note: this is an internal representation of the constraint, not the exact string given). Make sure the constraint was created first." + end + elsif constraint = @constraints[internal_ident] + if debug + p "status:" + p " existing (for modification)" + end + constraint.constant = res_constant + else + constraint = NSLayoutConstraint.constraintWithItem(res_item, + attribute:res_item_attribute, + relatedBy:res_related_by, + toItem:res_to_item, + attribute:res_to_item_attribute, + multiplier:res_multiplier, + constant:res_constant) + if debug + p "status:" + p " created" + end + @constraints[internal_ident] = constraint + if res_priority + constraint.priority = res_priority + end + @view.addConstraint(constraint) end if debug p "implemented:" p " #{constraint.description}" end - @view.addConstraint(constraint) constraint end + # removes the constraint matching equation string. constant is not considered. + # if no matching constraint is found, it will raise an exception. + def xeq(str) + eq(str, true) + end + + # transforms an NSLayoutConstraint into a string. this string is for debugging and produces + # a verbose translation. its not meant to be copied directly as an equation. def describe(constraint) subviews_inverse = subviews.invert item = subviews_inverse[constraint.firstItem] item_attribute = ATTRIBUTE_LOOKUP_INVERSE[constraint.firstAttribute] related_by = RELATED_BY_LOOKUP_INVERSE[constraint.relation]