Skip to content

Commit

Permalink
Merge pull request #17 from bmichotte/always-popover
Browse files Browse the repository at this point in the history
Always popover
  • Loading branch information
GantMan committed Jul 30, 2015
2 parents 85c481d + 0a7f81f commit e6fb099
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 28 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ You can even use the `make_button` helper to create custom buttons to add:
rmq.app.alert(title: "Actions!", message: "Actions created with `make_button` helper.", actions: button_list)
```


If you want to present your alert in :sheet style and you are on iPad, you have to provide the `:source` for the popover (either a UIView or a UIBarButtonItem)
```ruby
rmq.append(UIButton, :my_button).on(:tap) do |sender|
rmq.app.alert(title: "Actions!", message: "Alert from a Popover.", actions: [:ok, :cancel], style:sheet, source: sender)
end
```

*iOS 8+ options*
These options work only on iOS 8+
* `:modal` will prevent the popover to be close by tapping outside the popover
* `:arrow_direction` will force the direction of the popover arrow. Valid values are `:up`, `:down`, `:left`, `:right` or `:any`


## Available Templates

Button templates are provided [HERE](https://github.com/GantMan/RedAlert/blob/master/lib/project/button_templates.rb)
Expand Down
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Motion::Project::App.setup do |app|
app.identifier = 'com.gantlaborde.red_alert'
app.name = 'RedAlert'
app.deployment_target = ENV["DEPLOYMENT_TARGET"] if ENV["DEPLOYMENT_TARGET"]
app.device_family = [:iphone, :ipad]
end
27 changes: 17 additions & 10 deletions app/controllers/main_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ 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) do |action_type|
acs.append(UIButton, :alert_controller_four).on(:tap) do |sender|
rmq.app.alert(title: "Hey there!", message: "My style is :sheet", style: :sheet, source: sender) do |action_type|
puts "you clicked #{action_type}"
end
end

# Sheet with no message
acs.append(UIButton, :alert_controller_no_message).on(:tap) do
rmq.app.alert(message: nil, style: :sheet, actions: :yes_no_cancel ) do |action_type|
acs.append(UIButton, :alert_controller_no_message).on(:tap) do |sender|
rmq.app.alert(message: nil, style: :sheet, actions: :yes_no_cancel, source: sender) do |action_type|
puts "you clicked #{action_type}"
end
end
Expand All @@ -59,14 +59,14 @@ def viewDidLoad
# Text field example with :input style.
acs.append(UIButton, :alert_controller_fields_one).on(:tap) do
rmq.app.alert(title: "Text Field", message: "My style is :input", style: :input) do |action_type, fields|
puts "you entered '#{fields[:text].text}"
puts "you entered '#{fields[:text].text}'"
end
end

# Text field example with :input style and placeholder.
acs.append(UIButton, :alert_controller_fields_two).on(:tap) do
rmq.app.alert(title: "Text Field", message: "My style is :input", style: :input, placeholder: "Some Placeholder") do |action_type, fields|
puts "you entered '#{fields[:text].text}"
puts "you entered '#{fields[:text].text}'"
end
end

Expand Down Expand Up @@ -129,7 +129,7 @@ def viewDidLoad
end

# Alert example with 4 buttons, each made with `make_button` helper.
acs.append(UIButton, :custom_actions_helper_sheet).on(:tap) do
acs.append(UIButton, :custom_actions_helper_sheet).on(:tap) do |sender|
ok = rmq.app.make_button {
puts "OK pressed"
}
Expand All @@ -148,7 +148,14 @@ def viewDidLoad

button_list = [ok, yes, cancel, destructive]

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

# Alert from popover
acs.append(UIButton, :alert_from_popover).on(:tap) do |sender|
label = rmq.find(:template_tour).get
label.sizeToFit
rmq.app.alert(title: "Popover", message: "Presented from popover (if iPad)", actions: [:ok], style: :sheet, source: label, arrow_direction: [:left,:right])
end

acs.append(UILabel, :template_tour)
Expand All @@ -166,8 +173,8 @@ def viewDidLoad
end
end

acs.append(UIButton, :alert_controller_yesnocancel).on(:tap) do
rmq.app.alert(message: "Would you like a sandwich?", actions: :yes_no_cancel, style: :sheet) do |title|
acs.append(UIButton, :alert_controller_yesnocancel).on(:tap) do |sender|
rmq.app.alert(message: "Would you like a sandwich?", actions: :yes_no_cancel, style: :sheet, source: sender) do |title|
case title
when :yes
puts "Here's your Sandwich!"
Expand Down
5 changes: 5 additions & 0 deletions app/stylesheets/main_stylesheet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ def custom_actions_helper_sheet st
st.text = "Custom Sheet Actions"
end

def alert_from_popover st
basic_button(st)
st.text = "Alert From Popover on iPad"
end

def usage_tour st
st.frame = {bp: 10, w: screen_width - 20, l: 10, h:20}
st.clips_to_bounds = false
Expand Down
14 changes: 13 additions & 1 deletion lib/project/action_sheet_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ def build(actions, fieldset=nil, opts={})
@actions = actions
@opts = opts

raise ArgumentError.new "Please provide a :source view to use :sheet on iPad" if rmq.device.ipad? and !@opts[:source]

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

Expand Down Expand Up @@ -43,7 +45,17 @@ def build(actions, fieldset=nil, opts={})
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)

if rmq.device.ipad?
source = @opts[:source]
if source.is_a?(UIBarButtonItem)
@action_sheet.showFromBarButtonItem(source, animated: true)
else
@action_sheet.showFromRect(source.frame, inView: @view_controller.view, animated: true)
end
else
@action_sheet.showInView(@view_controller.view)
end
end

private
Expand Down
10 changes: 9 additions & 1 deletion lib/project/alert_constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,13 @@ module AlertConstants
destructive: UIAlertActionStyleDestructive
}

ALERT_POPOVER_ARROW_DIRECTION = {
up: UIPopoverArrowDirectionUp,
down: UIPopoverArrowDirectionDown,
left: UIPopoverArrowDirectionLeft,
right: UIPopoverArrowDirectionRight,
any: UIPopoverArrowDirectionAny
}

end
end
end
28 changes: 28 additions & 0 deletions lib/project/alert_controller_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,34 @@ def build(actions, fieldset = nil, opts={})
@alert_controller.addAction action
end

# popover
if @opts[:style] == :sheet and rmq.device.ipad?
raise ArgumentError.new "Please provide a :source view to use :sheet on iPad" unless @opts[:source]
source = @opts[:source]
@alert_controller.setModalPresentationStyle(UIModalPresentationPopover)
if @opts[:modal]
@alert_controller.setModalInPopover(true)
end
if source.is_a?(UIBarButtonItem)
@alert_controller.popoverPresentationController.barButtonItem = source
else
@alert_controller.popoverPresentationController.sourceView = source
end
@alert_controller.popoverPresentationController.sourceRect = source.bounds

if @opts[:arrow_direction]
directions = @opts[:arrow_direction]
unless directions.is_a?(Array)
directions = [directions]
end
arrow_direction = 0
directions.each do |direction|
arrow_direction |= RubyMotionQuery::AlertConstants::ALERT_POPOVER_ARROW_DIRECTION[direction]
end
@alert_controller.popoverPresentationController.permittedArrowDirections = arrow_direction
end
end

self
end

Expand Down
5 changes: 4 additions & 1 deletion lib/project/red_alert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ def alert(opts={}, &block)
# ------------------------------------
opts = {message: opts} if opts.is_a? String
opts = {style: :alert, animated: true, show_now: true}.merge(opts)

# Ability to make no message
opts[:message] = if opts.has_key?(:message)
opts[:message].nil? ? nil : opts[:message].to_s
else
NSLocalizedString("Alert!", nil)
end

opts[:style] = VALID_STYLES.include?(opts[:style]) ? opts[:style] : :alert
if opts[:style] == :sheet and rmq.device.ipad? and opts[:source].nil?
raise ArgumentError.new "Please provide a :source view to use :sheet on iPad"
end
opts[:fields] = opts[:style] == :custom && opts[:fields] ? opts[:fields] : {text: {placeholder: ''}}
api = rmq.device.ios_at_least?(8) ? :modern : :deprecated
api = :deprecated if rmq.device.ios_at_least?(8) && opts[:api] == :deprecated
Expand Down
4 changes: 2 additions & 2 deletions resources/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"Yes" = "Oui";
"No" = "Pas";
"No" = "Non";
"Cancel" = "Annuler";
"OK" = "OK";
"Delete" = "Effacer";
"Alert!" = "Alerte!";
"Login" = "S'identifier";
"Login" = "Identifiant";
"Password" = "Mot De Passe";
"Current Password" = "Mot De Passe Actuel";
"New Password" = "Nouveau Mot De Passe";
12 changes: 9 additions & 3 deletions spec/action_sheet_provider_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@ok = RubyMotionQuery::AlertAction.new(title: "OK", tag: :ok, style: :default)
@cancel = RubyMotionQuery::AlertAction.new(title: "Cancel", tag: :cancel, style: :cancel)
@boom = RubyMotionQuery::AlertAction.new(title: "Boom!", tag: :boom, style: :destructive)
@view = UIView.new
end

it "should prevent nil actions" do
Expand All @@ -16,10 +17,15 @@
Proc.new { @p.build([]) }.should.raise(ArgumentError)
end

it "should raise an error on iPad but not on iPhone" do
Proc.new { @p.build([@ok], nil, style: :sheet) }.should.raise(ArgumentError) if rmq.device.ipad?
Proc.new { @p.build([@ok], nil, style: :sheet) }.should.not.raise(ArgumentError) if rmq.device.iphone?
end

describe "action sheet with ok button" do

before do
@p.build [@ok], nil, title: "title"
@p.build [@ok], nil, title: "title", source: @view
end

it "should have the right title" do
Expand Down Expand Up @@ -51,7 +57,7 @@
describe "action sheet with a cancel button" do

before do
@p.build [@cancel], nil, title: "title"
@p.build [@cancel], nil, title: "title", source: @view
end

it "should have 1 button" do
Expand All @@ -75,7 +81,7 @@
describe "action sheet with a destructive button" do

before do
@p.build [@boom], nil, title: "title"
@p.build [@boom], nil, title: "title", source: @view
end

it "should have 1 button" do
Expand Down
7 changes: 6 additions & 1 deletion spec/alert_controller_provider_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@
Proc.new { @p.build([]) }.should.raise(ArgumentError)
end

it "should raise an error on iPad but not on iPhone" do
Proc.new { @p.build([@ok], nil, style: :sheet) }.should.raise(ArgumentError) if rmq.device.ipad?
Proc.new { @p.build([@ok], nil, style: :sheet) }.should.not.raise(ArgumentError) if rmq.device.iphone?
end

context 'without fields' do

describe "alert controller with ok button" do
Expand All @@ -119,7 +124,7 @@
describe "alert controller with a cancel button" do

before do
@p.build [@cancel], nil, title: "title", style: :sheet
@p.build [@cancel], nil, title: "title", style: :sheet, source: UIView.new
end

behaves_like "an alert controller with a cancel button"
Expand Down
4 changes: 2 additions & 2 deletions spec/i18n_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ def NSLocalizedString(key, comment)
end

it "should have an english No button" do
@ac.actions[1].title.should == "Pas"
@ac.actions[1].title.should == "Non"
end

it "should have an english Cancel button" do
@ac.actions[2].title.should == "Annuler"
end

it 'should have French placeholder text for the first field' do
@ac.textFields[0].placeholder.should == "S'identifier"
@ac.textFields[0].placeholder.should == "Identifiant"
end

it 'should have French placeholder text for the second field' do
Expand Down
20 changes: 13 additions & 7 deletions spec/red_alert_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@
before do
wait TEST_DELAY do
UIView.setAnimationsEnabled false
@view = UIView.new
@vc = rmq.view_controller
@provider = rmq.app.alert(message: "hello", show_now: false, animated: false, style: :sheet, api: :deprecated)
@provider = rmq.app.alert(message: "hello", show_now: false, animated: false, style: :sheet, api: :deprecated, source: @view)
end
end

Expand All @@ -70,20 +71,25 @@
@provider.action_sheet.class.should == UIActionSheet
end

it "should raise an error on iPad but not on iPhone" do
Proc.new { rmq.app.alert(style: :sheet) }.should.raise(ArgumentError) if rmq.device.ipad?
Proc.new { rmq.app.alert(style: :sheet) }.should.not.raise(ArgumentError) if rmq.device.iphone?
end

it "has a valid blank title" do
rmq.app.alert(show_now: false, animated: false, style: :sheet, api: :deprecated).action_sheet.title.should == "Alert!"
rmq.app.alert(show_now: false, animated: false, style: :sheet, api: :deprecated, source: @view).action_sheet.title.should == NSLocalizedString("Alert!", nil)
end

it "has a valid title when given" do
rmq.app.alert(title: "hi", show_now: false, animated: false, style: :sheet, api: :deprecated).action_sheet.title.should == "hi"
rmq.app.alert(title: "hi", show_now: false, animated: false, style: :sheet, api: :deprecated, source: @view).action_sheet.title.should == "hi"
end

it "should transfer message over to title when there is no title" do
rmq.app.alert(message: "hi", show_now: false, animated: false, style: :sheet, api: :deprecated).action_sheet.title.should == "hi"
rmq.app.alert(message: "hi", show_now: false, animated: false, style: :sheet, api: :deprecated, source: @view).action_sheet.title.should == "hi"
end

it "should never overwrite title with message" do
rmq.app.alert(title: "1", message: "2", show_now: false, animated: false, style: :sheet, api: :deprecated).action_sheet.title.should == "1"
rmq.app.alert(title: "1", message: "2", show_now: false, animated: false, style: :sheet, api: :deprecated, source: @view).action_sheet.title.should == "1"
end

it "should be visible at the right time" do
Expand Down Expand Up @@ -167,8 +173,8 @@
end

it 'has the correct placeholder text for the change password template fields' do
@provider.alert_view.textFieldAtIndex(0).placeholder.should == "Current Password"
@provider.alert_view.textFieldAtIndex(1).placeholder.should == "New Password"
@provider.alert_view.textFieldAtIndex(0).placeholder.should == NSLocalizedString("Current Password", nil)
@provider.alert_view.textFieldAtIndex(1).placeholder.should == NSLocalizedString("New Password", nil)
end

end
Expand Down

0 comments on commit e6fb099

Please sign in to comment.