Skip to content
Merged

7 #3

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
PATH
remote: .
specs:
RedAlert (0.3)
ruby_motion_query (>= 1.2.0)
RedAlert (0.3.0)
ruby_motion_query (>= 1.3.4)

GEM
remote: https://rubygems.org/
specs:
rake (10.4.2)
ruby_motion_query (1.2.0)
ruby_motion_query (1.3.4)

PLATFORMS
ruby
Expand Down
48 changes: 26 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
<img src="./_art/logo.png" alt="RedAlert Logo" width="100" />
[![image](http://ir_wp.s3.amazonaws.com/wp-content/uploads/sites/19/2014/09/rmq_plugin.png)](http://rubymotionquery.com)
[![image](http://ir_wp.s3.amazonaws.com/wp-content/uploads/sites/19/2014/09/rmq_plugin.png)](http://rubymotionquery.com)

# RedAlert
[![Gem Version](https://badge.fury.io/rb/RedAlert.svg)](http://badge.fury.io/rb/RedAlert) [![Build Status](https://travis-ci.org/GantMan/RedAlert.svg)](https://travis-ci.org/GantMan/RedAlert) _Alerts and ActionSheets with ease_

### Did you know that UIAlertView and UIActionSheet (as well as their respective delegate protocols) are deprecated in iOS 8?

Apple requests you start using the new `UIAlertController`. This gem is built on `UIAlertController` and RMQ, along with support for antiquated `UIAlertView`s for the gnostalgic.
Apple requests you start using the new `UIAlertController`. This gem is built on `UIAlertController` and RMQ, along with seemless support for antiquated `UIAlertView`s & `UIActionSheet`s for the gnostalgic.

With an emphasis on ease of use, this gem allows you to quickly implement Alerts and Actionsheets in your RMQ RubyMotion applications.

When run on iOS 8, RedAlert uses `UIAlertController` to present alerts and sheets.

When run on iOS 7, RedAlert uses `UIAlertView` present alerts and `UIActionSheet` to present sheets.

## Screenshot

<img src="./_art/screen.png" alt="Screen Shot" width="500" />

## Installation

**Requires RMQ 1.2.0 or later, and iOS 8 or later**
**Requires RMQ 1.2.0 or later, and iOS 7 or later**

Add the **RedAlert** gem to your Gemfile.
```ruby
Expand Down Expand Up @@ -78,33 +82,33 @@ Templates are provided [HERE](https://github.com/GantMan/RedAlert/blob/master/li

_More to come:_ be sure to submit a pull-request with your button template needs.


## iOS 7 Support

If you still need iOS 7, RedAlert has your back.

Instead of using iOS 8's UIAlertController, RedAlert will use UIActionSheet to display your sheets and
UIAlertView to display your views.

**With little-to-no changes to your code.**

Because capabilities of iOS 7 & 8 alert-components are different, just a few edge cases that might sting you:

* `UIAlertView` doesn't have the concept of :destructive buttons. These will fall back to :default.
* `UIAlertView` cares about the order of your `:cancel` actions, so `[:ok, :cancel]` is shown different than `[:cancel, :ok]`.
* `UIActionSheet` also cares about the order. It's possible to put a `:cancel` first, which looks slightly awkward when shown. Try to put `:cancel` last.
* `UIAlertView`'s `alertViewStyles` are not available through RedAlert as they aren't compatible with iOS 8. You'll have to call that directly.


## More info

**i18n support by [Mark Rickert](https://github.com/GantMan/RedAlert/pull/2)**
**iOS 7 support by Steve Kellock**

Feel free to read up on UIAlertController to see what all is wrapped up in this gem.
* [Hayageek](http://hayageek.com/uialertcontroller-example-ios/)
* [NSHipster](http://nshipster.com/uialertcontroller/)

## Classic UIAlertView Helpers

If you'd like to still support pre-iOS8, you can easily use `rmq.app.alert_view` with a similar syntax, and instead of actions you'll used the predefined delegates.

**`UIAlertView` Classic:**
```ruby
# support the elderly
rmq.app.alert_view("Hey look at this old trick")

# Still feels like magic!
rmq.app.alert_view({
title: "Hey There",
message: "Check out this complex alert!",
cancel_button: 'Nevermind',
other_buttons: ['Log In'],
delegate: nil,
view_style: UIAlertViewStyleLoginAndPasswordInput
})
```

## Contributing

Expand Down
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ Motion::Project::App.setup do |app|
# Use `rake config' to see complete project settings.
app.identifier = 'com.gantlaborde.red_alert'
app.name = 'RedAlert'
app.deployment_target = ENV["DEPLOYMENT_TARGET"] if ENV["DEPLOYMENT_TARGET"]
end
57 changes: 23 additions & 34 deletions app/controllers/main_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ def viewDidLoad
# Simple action sheet example.
# OK button that doesn't care when pressed.
acs.append(UIButton, :alert_controller_four).on(:tap) do
rmq.app.alert(title: "Hey there!", message: "My style is :sheet", style: :sheet)
rmq.app.alert(title: "Hey there!", message: "My style is :sheet", style: :sheet) do |action_type|
puts "you clicked #{action_type}"
end
end

##############################
Expand All @@ -49,7 +51,7 @@ def viewDidLoad


# Alert example with 4 buttons, each made with `make_button` helper.
acs.append(UIButton, :custom_actions_helper).on(:tap) do
acs.append(UIButton, :custom_actions_helper_alert).on(:tap) do
ok = rmq.app.make_button {
puts "OK pressed"
}
Expand All @@ -71,24 +73,28 @@ def viewDidLoad
rmq.app.alert(title: "Actions!", message: "Actions created with `make_button` helper.", actions: button_list)
end

# Example of loading the actions array with native UIAlertAction objects.
acs.append(UIButton, :alert_controller_advanced_button).on(:tap) do
# Alert example with 4 buttons, each made with `make_button` helper.
acs.append(UIButton, :custom_actions_helper_sheet).on(:tap) do
ok = rmq.app.make_button {
puts "OK pressed"
}

ok = UIAlertAction.actionWithTitle("OK", style: UIAlertActionStyleDefault, handler: -> (action) {
puts "#{action.title} was pressed"
})
yes = rmq.app.make_button("Yes") {
puts "Yes pressed"
}

cancel = UIAlertAction.actionWithTitle("Cancel", style: UIAlertActionStyleCancel, handler: -> (action) {
puts "#{action.title} was pressed"
})
cancel = rmq.app.make_button(title: "Cancel", style: :cancel) {
puts "Cancel pressed"
}

delete = UIAlertAction.actionWithTitle("Delete", style: UIAlertActionStyleDestructive, handler: -> (action) {
puts "#{action.title} was pressed"
})
destructive = rmq.app.make_button(title: "Destructive", style: :destructive) {
puts "Destructive pressed"
}

rmq.app.alert(title: "More Actions", message: "UIViewController Actions", actions: [ok, cancel, delete])
end
button_list = [ok, yes, cancel, destructive]

rmq.app.alert(title: "Actions!", message: "Actions created with `make_button` helper.", actions: button_list, style: :sheet)
end

acs.append(UILabel, :template_tour)

Expand All @@ -112,6 +118,8 @@ def viewDidLoad
puts "Here's your Sandwich!"
when :no
puts "FINE!"
when :cancel
puts "You hit cancel"
end
end
end
Expand Down Expand Up @@ -142,25 +150,6 @@ def viewDidLoad
end.resize_frame_to_fit_subviews(bottom: 10, right: -5)


# Classic UIAlertView Examples
cs.append(UIView, :alert_view_section).tap do |avs|
avs.append(UILabel, :alert_view_title)

avs.append(UIButton, :alert_view_button).on(:tap) do
rmq.app.alert_view("Minimal UIAlertView")
end

avs.append(UIButton, :alert_view_ks_button).on(:tap) do
rmq.app.alert_view({
title: "Hey There",
message: "Check out this complex alert!",
cancel_button: 'Nevermind',
other_buttons: ['Log In'],
delegate: nil,
view_style: UIAlertViewStyleLoginAndPasswordInput
})
end
end.resize_frame_to_fit_subviews(bottom: 10, right: -5)

end.resize_content_to_fit_subviews
end
Expand Down
29 changes: 25 additions & 4 deletions app/stylesheets/main_stylesheet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ def alert_controller_four st
st.text = "Title and :sheet Style"
end

def custom_actions_helper st
def custom_actions_helper_alert st
basic_button(st)
st.text = "Custom Actions via Helper"
st.text = "Custom Alert Actions"
end

def alert_controller_advanced_button st
def custom_actions_helper_sheet st
basic_button(st)
st.text = "Using UIAlertAction"
st.text = "Custom Sheet Actions"
end

def usage_tour st
Expand Down Expand Up @@ -111,4 +111,25 @@ def alert_controller_deletecancel st
st.text = ":delete_cancel Template"
end

def action_sheet_section st
st.frame = {bp: 20, w: screen_width - 5, centered: :horizontal}
st.border_color = color.from_rgba(0,0,0,0.5)
st.border_width = 1
st.corner_radius = 5
st.background_color = color.from_rgba(0, 0, 0, 0.2)
end

def action_sheet_title st
st.frame = {bp: 10, w: screen_width, h: 40}
st.text_alignment = :centered
st.number_of_lines = 2
st.text = "UIActionSheet Classic (Deprecated)"
end

def action_sheet_button st
basic_button(st)
st.text = "UIActionSheet"
end


end
62 changes: 62 additions & 0 deletions lib/project/action_sheet_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
module RubyMotionQuery

# Brings the "I'm not dead yet" iOS 7 UIActionSheet to RedAlert.
class ActionSheetProvider

attr_reader :action_sheet

def build(actions, opts={})
raise ArgumentError.new "At least 1 action is required." unless actions && actions.length > 0
@actions = actions
@opts = opts

# grab the first cancel action
cancel_action = actions.find { |action| action.cancel? }

# grab the first destructive action (UIActionSheet only supports one)
destructive_action = actions.find { |action| action.destructive? }

# let's put our actions in the correct display order for our callback
@actions_in_display_order = actions.find_all { |action| action.default? }
@actions_in_display_order << destructive_action if destructive_action
@actions_in_display_order << cancel_action if cancel_action

# create our action sheet
@action_sheet = UIActionSheet.alloc.initWithTitle(
@opts[:title] || @opts[:message],
delegate: self,
cancelButtonTitle: nil,
destructiveButtonTitle: nil,
otherButtonTitles: nil
)

# then append our other buttons in later
@actions_in_display_order.each { |action| @action_sheet.addButtonWithTitle(action.title) }

# mark where our special buttons are
@action_sheet.destructiveButtonIndex = @actions_in_display_order.index { |action| action.destructive? } || -1
@action_sheet.cancelButtonIndex = @actions_in_display_order.index { |action| action.cancel? } || -1

self
end

def show
# when we show, the view controller will disappear because a different _UIAlertOverlayWindow window will take its place
@view_controller = rmq.view_controller
@action_sheet.showInView(@view_controller.view)
end

private

# fires when we were dismissed <UIActionSheetDelegate>
def actionSheet(actionSheet, didDismissWithButtonIndex:buttonIndex)
# pull from the view controller instance
@view_controller.dismissViewControllerAnimated @opts[:animated], completion: nil
action = @actions_in_display_order[buttonIndex]
action.handler.call(action.tag) if action.handler
@view_controller = nil # forget the reference
end

end

end
32 changes: 32 additions & 0 deletions lib/project/alert_action.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module RubyMotionQuery
class AlertAction

VALID_STYLES = [:default, :destructive, :cancel]

attr_reader :title
attr_reader :tag
attr_reader :style
attr_reader :handler

def initialize(opts = {}, &block)
opts = {title: opts} if opts.is_a? String
@title = opts[:title] || NSLocalizedString("OK", nil)
@tag = opts[:tag] || @title.gsub(/\s+/,"_").downcase.to_sym
@style = VALID_STYLES.include?(opts[:style]) ? opts[:style] : VALID_STYLES.first
@handler = block if block_given?
end

def default?
@style == :default
end

def destructive?
@style == :destructive
end

def cancel?
@style == :cancel
end

end
end
43 changes: 43 additions & 0 deletions lib/project/alert_controller_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module RubyMotionQuery

# The new dual-purpose iOS 8 UIAlertControllerProvider
class AlertControllerProvider

attr_reader :alert_controller

def build(actions, opts={})
raise ArgumentError.new("At least 1 action is required.") unless actions && actions.length > 0
@actions = actions
@opts = opts

# create our alert controller
style = RubyMotionQuery::AlertConstants::ALERT_TYPES[@opts[:style]]
@alert_controller = UIAlertController.alertControllerWithTitle @opts[:title], message:@opts[:message], preferredStyle: style

# load up the UIAlertController's actions
@actions.each do |alert_action|

# convert the style
ios_style = RubyMotionQuery::AlertConstants::ALERT_ACTION_STYLE[alert_action.style]

# convert the callback
handler = lambda do |action|
alert_action.handler.call(alert_action.tag) unless alert_action.handler.nil?
end if alert_action.handler

# create teh action
action = UIAlertAction.actionWithTitle alert_action.title, style: ios_style, handler: handler

# add it to the UIAlertController
@alert_controller.addAction action
end

self
end

def show
rmq.view_controller.presentViewController(@alert_controller, animated: @opts[:animated], completion: nil)
end

end
end
Loading