clearsightstudio/ProMotion

View on GitHub
lib/ProMotion/styling/styling.rb

Summary

Maintainability
A
1 hr
Test Coverage
module ProMotion
  module Styling
    def set_attributes(element, args = {})
      args = get_attributes_from_symbol(args)
      ignore_keys = [:transition_style, :presentation_style]
      args.each do |k, v|
        set_attribute(element, k, v) unless ignore_keys.include?(k)
      end
      element.send(:on_styled) if element.respond_to?(:on_styled)
      element
    end

    def set_attribute(element, k, v)
      return unless element

      if !element.is_a?(CALayer) && v.is_a?(Hash) && element.respond_to?("#{k}=")
        element.send("#{k}=", v)
      elsif v.is_a?(Hash) && element.respond_to?(k)
        sub_element = element.send(k)
        set_attributes(sub_element, v) if sub_element
      elsif element.respond_to?("#{k}=")
        element.send("#{k}=", v)
      elsif v.is_a?(Array) && element.respond_to?("#{k}") && element.method("#{k}").arity == v.length
        element.send("#{k}", *v)
      elsif k.to_s.include?("_") # Snake case?
        set_attribute(element, camelize(k), v)
      else # Warn
        mp "set_attribute: #{element.inspect} does not respond to #{k}=.", force_color: :purple
        # TODO - remove now, or when fully deprecated - there will be no verbose
        # check when logger is removed
        mp "BACKTRACE", caller(0).join("\n") if PM.logger.level == :verbose
      end
      element
    end

    def content_max(view, mode = :height)
      view.subviews.map do |sub_view|
        if sub_view.isHidden
          0
        elsif mode == :height
          sub_view.frame.origin.y + sub_view.frame.size.height
        else
          sub_view.frame.origin.x + sub_view.frame.size.width
        end
      end.max.to_f
    end

    def content_height(view)
      content_max(view, :height)
    end

    def content_width(view)
      content_max(view, :width)
    end

    # iterate up the view hierarchy to find the parent element
    # of "type" containing this view
    def closest_parent(type, this_view = nil)
      this_view ||= view_or_self.superview
      while this_view != nil do
        return this_view if this_view.is_a? type
        this_view = this_view.superview
      end
      nil
    end

    def add(element, attrs = {})
      add_to view_or_self, element, attrs
    end

    def remove(elements)
      Array(elements).each(&:removeFromSuperview)
    end

    def add_to(parent_element, elements, attrs = {})
      attrs = get_attributes_from_symbol(attrs)
      Array(elements).each do |element|
        parent_element.addSubview element
        set_attributes(element, attrs) if attrs && attrs.length > 0
        element.send(:on_load) if element.respond_to?(:on_load)
      end
      elements
    end

    def view_or_self
      self.respond_to?(:view) ? self.view : self
    end

    # These three color methods are stolen from BubbleWrap.
    def rgb_color(r,g,b)
      rgba_color(r,g,b,1)
    end

    def rgba_color(r,g,b,a)
      r,g,b = [r,g,b].map { |i| i / 255.0}
      UIColor.colorWithRed(r, green: g, blue:b, alpha:a)
    end

    def hex_color(str)
      hex_color = str.gsub("#", "")
      case hex_color.size
      when 3
        colors = hex_color.scan(%r{[0-9A-Fa-f]}).map{ |el| (el * 2).to_i(16) }
      when 6
        colors = hex_color.scan(%r<[0-9A-Fa-f]{2}>).map{ |el| el.to_i(16) }
      else
        raise ArgumentError
      end

      raise ArgumentError unless colors.size == 3
      rgb_color(colors[0], colors[1], colors[2])
    end

    # Turns a snake_case string into a camelCase string.
    def camelize(str)
      str.split('_').inject([]){ |buffer,e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
    end

  protected

    def get_attributes_from_symbol(attrs)
      return attrs if attrs.is_a?(Hash)
      mp("#{attrs} styling method is not defined", force_color: :red) unless self.respond_to?(attrs)
      new_attrs = send(attrs)
      mp("#{attrs} should return a hash", force_color: :red) unless new_attrs.is_a?(Hash)
      new_attrs
    end

    def map_resize_symbol(symbol)
      @_resize_symbols ||= {
        left:     UIViewAutoresizingFlexibleLeftMargin,
        right:    UIViewAutoresizingFlexibleRightMargin,
        top:      UIViewAutoresizingFlexibleTopMargin,
        bottom:   UIViewAutoresizingFlexibleBottomMargin,
        width:    UIViewAutoresizingFlexibleWidth,
        height:   UIViewAutoresizingFlexibleHeight
      }
      @_resize_symbols[symbol] || symbol
    end

  end
end