Stop ActionSheet from closing in Ionic 3 - ionic-framework

I have an ActionSheet in Ionic 3 and the last button of that is for Displaying more option. When the button is clicked 2 more buttons should be added on the ActionSheet. The problem is when I clicked any button, the ActionSheet is getting closed. I could not find out a way to stop it from closing.
Is there any way to stop ActionSheet from closing?
onMore(){
let actionSheet = this.actionSheetCtrl.create({
buttons: [
{
text: 'Option 1',
handler: () => {
}
},
{
text: 'More',
handler: () => {
this.showMore(actionSheet);
}
}
]
});
actionSheet.present();
}
private showMore(actionSheet){
actionSheet.addButton({
text: 'Option 2',
handler: () => {
}
});
}

It is simple.
Just add return false to your handler like below.
onMore(){
let actionSheet = this.actionSheetCtrl.create({
buttons: [
{
text: 'Option 1',
handler: () => {
}
},
{
text: 'More',
handler: () => {
this.showMore(actionSheet);
return false;
}
}
]
});
actionSheet.present();
}
Stackblitz

Related

Why I am not able to use multiple `.alert` dialog in my SwiftUI project?

I want to delete list items and when I delete list items, it will show confirmation dialog like .alert dialog. I have code below and if I want to remove list item .alert dialog is work, but if I try to remove all list items, .alert dialog not work, and I am not able to remove all items, I do not know where I missed? I guess most probably it is due to the I have two .alert dialog and they are conflicted, any idea?
struct CustomView: View {
#State private var selectedUsers: CustomModel?
#State var users: [CustomModel]
#State private var selectDelete = false
#State private var selectAllDelete = false
var body: some View {
ScrollView(.vertical, showsIndicators: false, content: {
VStack(content: {
ForEach(users){ user in
CustomRowView(user: user)
.contextMenu {
Button(action: {
selectDelete = true
}) {
Text("remove")
}
Button(action: {
selectAllDelete = true
}) {
Text("remove all")
}
}
.alert(isPresented: $selectDelete) {
Alert(title: Text("title"),
message: Text("message"),
primaryButton: .destructive(Text("Delete")) {
self.delete(item: data)
},
secondaryButton: .cancel()
)
}
.alert(isPresented: $selectAllDelete) {
Alert(title: Text("title"),
message: Text("message"),
primaryButton: .destructive(Text("Delete")) {
self.datas.removeAll()
},
secondaryButton: .cancel()
)
}
.onDelete { (indexSet) in
self.users.remove(atOffsets: indexSet)
}
}
})
})
}
private func delete(item user: CustomModel) {
if let index = users.firstIndex(where: { $0.id == user.id }) {
users.remove(at: index)
}
}
}
model:
struct CustomModel: Identifiable{
var id = UUID().uuidString
var name: String
}
var users = [
CustomModel(name: "david"),
CustomModel(name: "marry"),
CustomModel(name: "henry"),
CustomModel(name: "nadi"), ]
You can create an alert type and handle it using switch statement.
enum AlertType {
case selectDelete
case selectAllDelete
}
private var alertType: AlertType?
#State private var isAlertPresented = false
...
Button(action: {
alertType = .selectDelete
isAlertPresented = true
}) {
Text("remove all")
}
...
.alert(isPresented: $isAlertPresented) {
presentAlert()
}
...
func presentAlert() -> Alert {
switch alertType {
case .selectDelete:
return Alert(title: Text("title"),
message: Text("message"),
primaryButton: .destructive(Text("Delete")) {
self.delete(item: data)
},
secondaryButton: .cancel())
case .selectAllDelete:
return Alert(title: Text("title"),
message: Text("message"),
primaryButton: .destructive(Text("Delete")) {
self.datas.removeAll()
},
secondaryButton: .cancel())
default:
return Alert(title: Text(""))
}
}
If you apply the modifier to each Button it'll work. Also, you might find confirmationDialog more suitable for this task.
Move your Buttons into custom Views will help too because body has a 10 View limit.

Passing A Closure With Arguments To SwiftUI View

I am working on a SwiftUI project. I've created a custom button that I can pass a function to. This looks like the following.
Custom Button
struct CustomButton: View {
let buttonTitle: String
var function: () -> Void
var body: some View {
Button(action: {
self.function()
}, label: {
Text(self.buttonTitle)
}) // Button - Login
} // View
}
In the view that uses this I can do the following.
struct NewView: View {
var body: some View {
CustomButton(buttonTitle: "Custom Button", function: myFunc)
}
}
func myFunc() {
print("My Custom Button Tapped")
}
This works really well.
What I want to do now is pass a parameter to the function. And I am having trouble with this. I tried the following.
struct CustomButton: View {
let buttonTitle: String
var function: (String) -> Void
var body: some View {
Button(action: {
self.function() // I DON'T KNOW WHAT DO TO HERE.
}, label: {
Text(self.buttonTitle)
}) // Button - Login
} // View
}
struct NewView: View {
var body: some View {
CustomButton(buttonTitle: "Custom Button", function: myFunc(text: "Hello"))
}
}
func myFunc(text: String) {
print(text)
}
This does not work. When I call CustomButton I get the following error.
Cannot convert value of type '()' to expected argument type '() ->
Void'
I also do not know what parameter to add to the self.function() call in the Button action.
Any help would be greatly appreciated.
First, the simplest answer -- by enclosing myFunc(text: "Hello") in { }, you can turn it into a closure. Then, it can get passed to your original () -> Void declaration.
struct CustomButton: View {
let buttonTitle: String
let function : () -> Void
var body: some View {
Button(action: {
self.function()
}, label: {
Text(self.buttonTitle)
}) // Button - Login
} // View
}
struct NewView: View {
var body: some View {
CustomButton(buttonTitle: "Custom Button", function: {
myFunc(text: "Hello")
})
}
}
You could also use an #autoclosure to provide similar behavior without the { }, but you'd have to declare a custom init for your CustomButton:
struct CustomButton: View {
let buttonTitle: String
let function : () -> Void
init(buttonTitle: String, function: #autoclosure #escaping () -> Void) {
self.buttonTitle = buttonTitle
self.function = function
}
var body: some View {
Button(action: {
self.function()
}, label: {
Text(self.buttonTitle)
}) // Button - Login
} // View
}
struct NewView: View {
var body: some View {
CustomButton(buttonTitle: "Custom Button", function: myFunc(text:"Hello"))
}
}
Finally, another option (that I think there's unlikely to a use case for, but just in case it fits) would be to pass the string parameter separately:
struct CustomButton: View {
let buttonTitle: String
let stringParameter : String
let function : (String) -> Void
var body: some View {
Button(action: {
self.function(stringParameter)
}, label: {
Text(self.buttonTitle)
}) // Button - Login
} // View
}
struct NewView: View {
var body: some View {
CustomButton(buttonTitle: "Custom Button", stringParameter: "Hello", function: myFunc)
}
}
Here what you may looking for:
struct ContentView: View {
let action: (String) -> Void = { value in print(value) }
var body: some View {
CustomButtonView(string: "print", valueToSend: "Hello World!", action: action)
}
}
struct CustomButtonView: View {
let string: String
let valueToSend: String
let action: (String) -> Void
var body: some View {
Button(string) {
action(valueToSend)
}
}
}

SwiftUI – Not clear how to handle the alert with asynchronous methods

I have to present an Alert on a view if the user taps on it.
My alert depends on several situations:
Item is purchased. Show the item.
Item have not be purchased. Show an alert telling the user the item have to be purchased. This alert must show two buttons, OK to purchase, Cancel to dismiss.
User taps to purchase the item.
Purchase is successful, show the item.
Purchase fails, show error.
This is how I did it.
class AlertDialog {
enum SelectedType {
case none
case purchase
case mustBePurchased
case purchaseError
}
var selectedType:SelectedType = .none
}
struct FilteredListItem: View {
#State var showAlert: Bool = false
private var alertDialog:AlertDialog?
var body: some View {
Text(item.termLowerCase)
.font(fontItems)
.foregroundColor(.white)
.onTapGesture {
DispatchQueue.main.async {
appStoreWrapper.verifyPurchase(productID: item.package!)
{ // run if purchased
purchased = true
} runIfNotPurchased: {
purchased = false
alertDialog!.selectedType = .mustBePurchased
showAlert = true
}
}
}
.alert(isPresented: $showAlert) {
if alertDialog!.selectedType == .purchase {
appStoreWrapper.purchase(productID: item.package!) {
// run if purchased
purchased = true
} runIfPurchaseFailed: { (error) in
alertDialog!.selectedType = .purchaseError
appStoreWrapper.purchaseError = error
showAlert = true
}
} else if alertDialog!.selectedType == .purchaseError {
let primaryButton = Alert.Button.default(Text("OK")) {
showAlert = false
}
return Alert(title: Text(appStoreWrapper.makeString("ERROR")),
message: Text(appStoreWrapper.purchaseError),
dismissButton: primaryButton)
}
let dismissButton = Alert.Button.default(Text(appStoreWrapper.makeString("CANCEL"))) {
showAlert = false
}
let primaryButton = Alert.Button.default(Text("OK")) {
appStoreWrapper.purchase(productID: item.package!) {
// run if purchased
purchased = true
} runIfPurchaseFailed: { (error) in
appStoreWrapper.purchaseError = error
alertDialog!.selectedType = .purchaseError
showAlert = true
print(erro)
}
}
return Alert(title: Text(appStoreWrapper.makeString("ERROR")),
message: Text(appStoreWrapper.purchaseError),
primaryButton: primaryButton,
secondaryButton: dismissButton)
}
This is my problem: the modifier .alert(isPresented: $showAlert) expects an Alert() to be returned, right? But I have these asynchronous methods
appStoreWrapper.verifyPurchase(productID: item.package!)
{ // run if purchased },
runIfNotPurchased: { }
that cannot return anything to the alert modifier. How do I do that? Is what I am doing right?
There's a lot going on in your code and you didn't post the code for appStoreWrapper, but here's some code that should be able to point you in the right direction.
FYI:
You can use a Button with an Action instead of using Text with .onTapGesture
The code within .Alert should only function to get an Alert. You shouldn't be doing other actions within the .Alert closure.
struct FilteredListItem: View {
#State var showAlert: Bool = false
private var alertDialog: AlertDialog?
var body: some View {
Button(action: {
verifyItem()
}, label: {
Text("ITEM NAME")
.foregroundColor(.white)
})
.accentColor(.primary)
.alert(isPresented: $showAlert, content: {
getAlert()
})
}
func verifyItem() {
// FUNCTION TO VERIFY ITEM HERE
var success = true //appStoreWrapper.verifyPurchase...
if success {
// Handle success
} else {
alertDialog?.selectedType = .mustBePurchased
showAlert.toggle()
}
}
func purchaseItem() {
// FUNCTION TO PURCHASE ITEM HERE
var success = true //appStoreWrapper.purchase...
if success {
// Handle success
} else {
alertDialog?.selectedType = .purchaseError
showAlert.toggle()
}
}
func getAlert() -> Alert {
guard let dialog = alertDialog else {
return Alert(title: Text("Error getting alert dialog."))
}
switch dialog.selectedType {
case .purchaseError:
return Alert(
title: Text("Error purchasing item."),
message: nil,
dismissButton: .default(Text("OK")))
case .mustBePurchased:
return Alert(
title: Text("Items have to be purchased."),
message: nil,
primaryButton: .default(Text("Purchase"), action: {
purchaseItem()
}),
secondaryButton: .cancel())
case .none, .purchase:
return Alert(title: Text("Purchased!"))
}
}
}

Display an Alert inside a conditional with SwiftUI

I understand that an Alert can be presented as a function of a Button, but can an Alert be presented inside a conditional? Such as:
struct ContentView: View {
var body: some View {
Text("Hello World!")
}
}
if isValid {
//present alert
let alert = UIAlertController(title: "My Title", message: "This
is my message.", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style:
UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
With this I get
Value of type 'ContentView' has no member 'present'
I'm not sure why are you using UIKit. Here's an example of how an alert may be presented when something changes a flag. In this case, a two second timer:
import SwiftUI
class MyModel: ObservableObject {
#Published var isValid: Bool = false
init() {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
self.isValid = true
}
}
}
struct ContentView: View {
#ObservedObject var model: MyModel = MyModel()
var body: some View {
VStack {
Text("Some text")
Text("Some text")
Text("Some text")
Text("Some text")
}.alert(isPresented: $model.isValid, content: {
Alert(title: Text("Title"),
message: Text("Message"),
dismissButton: .default(Text("OK")) { print("do something") })
})
}
}

ionic 2 - Property 'dismiss' does not exist on type 'ActionSheetController'

I am following the doc trying to create Actionsheet. Not sure why getting the error message Property 'dismiss' does not exist on type 'ActionSheetController' on the dismiss() and Cannot find name someAsyncOperation for the someAsyncOperation().
Did I miss anything?
import { ActionSheetController } from 'ionic-angular';
import { IonicPage, NavController, NavParams, ModalController, ViewController } from 'ionic-angular';
constructor(
public viewCtrl: ViewController,
public navCtrl: NavController,
public actionSheetCtrl: ActionSheetController,
public modalCtrl: ModalController,
public navParams: NavParams,
) {}
openActSheet(){
let actionSheet = this.actionSheetCtrl.create({
title:"Type",
buttons:[
{
text: 'Hour',
handler: () => {
let navTransition = this.actionSheetCtrl.dismiss();
someAsyncOperation().then(() => {
console.log("text");
})
navTransition.then(() => {
this.navCtrl.pop();
});
}
},
{
text: 'Day',
handler: function(){
console.log("Day Clicked");
}
},
{
text: 'Week',
handler: function(){
console.log("Week Clicked");
}
},
{
text: 'Month',
handler: function(){
console.log("Month Clicked");
}
}
]
});
actionSheet.present();
}
ActionSheetController does not have dismiss() function. It is available in actionsheet Object.
Try:
openActSheet(){
let actionSheet = this.actionSheetCtrl.create({
title:"Type",
buttons:[
{
text: 'Hour',
handler: () => {
let navTransition = actionSheet.dismiss(); //here
//....