My expectation: I want to open a new full screen view (PhoneNumberView) on click of confirmation dialog button.
Reality: Nothing happens when I click to Back button (please check my code below)
Here is my code:
SetPasswordButton(value: "exitButton",
password: $password,
key: $key,
unlocked: $unlocked,
repeatPassword: $repeatPassword, isExitButtonTapped: $isExitButtonTapped)
.confirmationDialog("", isPresented: $isExitButtonTapped) {
Button("Back", role: .destructive) {
goToPhoneScreen = true
}
.fullScreenCover(isPresented: $goToPhoneScreen {
PhoneNumberView()
}
Button("Cancel", role: .cancel) {}
}
Could you help me?
You have misplaced the .fullScreenCover in your code, that should be something like this, i have tested it and it's working as expected, here is the Full code,
struct ContentView: View {
var body: some View {
SetPasswordButton(value: "exitButton",
password: $password,
key: $key,
unlocked: $unlocked,
repeatPassword: $repeatPassword, isExitButtonTapped: $isExitButtonTapped)
.confirmationDialog("", isPresented: $isExitButtonTapped) {
Button("Back") {
goToPhoneScreen = true
}
}
.fullScreenCover(isPresented: $goToPhoneScreen){
PhoneNumberView()
}
Button("Cancel", role: .cancel) {}
}
}
Related
I am creating a Form in SwiftUi with a section that is including a flexible number of instruction.
Next to the last instruction TextField, I am showing a "+"-Button that is extending the instructions array with a new member:
var body: some View {
NavigationView {
Form {
...
Section(header: Text("Instructions")) {
InstructionsSectionView(instructions: $recipeViewModel.recipe.instructions)
}
...
struct InstructionsSectionView: View {
#Binding var instructions: [String]
var body: some View {
ForEach(instructions.indices, id: \.self) { index in
HStack {
TextField("Instruction", text: $instructions[index])
if(index == instructions.count-1) {
addInstructionButton
}
}
}
}
var addInstructionButton: some View {
Button(action: {
instructions.append("")
}) {
Image(systemName: "plus.circle.fill")
}
}
}
Now the problem is, that the button click-area is not limited to the picture but to the whole last row. Precisely the part just around the textField, meaning if I click in it, I can edit the text, but if I click on the border somewhere, a new entry is added.
I assume that this is specific to Form {} (or also List{}), since it does not happen if I use a Button next to a text field in a "normal" set-up.
Is there something wrong with my code? Is this an expected behaviour?
I am not sure why border is getting tappable, but as a workaround I used plainButtonStyle and that seems to fix this issue, and keeps functionality intact .
struct TestView: View {
#State private var endAmount: CGFloat = 0
#State private var recipeViewModel = ["abc","Deef"]
var body: some View {
NavigationView {
Form {
Section(header: Text("Instructions")) {
InstructionsSectionView(instructions: $recipeViewModel)
}
}
}
}
}
struct InstructionsSectionView: View {
#Binding var instructions: [String]
var body: some View {
ForEach(instructions.indices, id: \.self) { index in
HStack {
TextField("Instruction", text: $instructions[index])
Spacer()
if(index == instructions.count-1) {
addInstructionButton
.buttonStyle(PlainButtonStyle())
.foregroundColor(.blue)
}
}
}
}
var addInstructionButton: some View {
Button(action: {
instructions.append("")
}) {
Image(systemName: "plus.circle.fill")
}
}
}
I am trying to do something very basic, but I have been through the official tutorials, and through dozens of stack overflow posts. I am trying to open an activity from a menu button. I do want the standard navigation back arrow when I do this.
var body: some View {
NavigationView {
VStack(alignment: .leading) {
Text("Hello World!")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Menu {
Button(action: {
Activities()
}) {
Label("Activities", systemImage: "doc")
}
"Activities()" is my view. When I do this nothing happens. I have read in other posts that you cannot have a navigation link in a menu either, which would be fine if that worked. How do I programmatically open up a view so the back arrow works?
Thank you
In this case where you want to open a view but can't use a NavigationLink directly you can use it in another place and activate it programmatically from the button via a State property:
#State private var isShowingDetailView = false
var body: some View {
NavigationView {
VStack(alignment: .leading) {
NavigationLink(destination: Activities(), isActive: $isShowingDetailView) {
EmptyView()
}
Text("Hello World!")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Menu {
Button(action: {
isShowingDetailView = true
}) {
Label("Activities", systemImage: "doc")
}
}
}
}
}
}
is there a way to get a destructive Button style in SwiftUI?
I know I can do this for a ContextMenu, but I did not find a way for "normal" Button.
Cheers
iOS 15
From iOS 15 You can (and you should!) assign a Role to each button like:
Button("Delete", role: .destructive) {
deleteSomething()
}
Assigning the role to a button helps the system to apply the proper style for each context that uses the button (for example like this example for a context menu)
More customization
You can create a combination of modifiers to create the needed style.
Demo
Code for above example:
VStack {
Button("Plain", role: .none, action: { })
.buttonStyle(PlainButtonStyle())
Button("Automatic", role: .none, action: { })
.buttonStyle(.automatic)
Button("Log out", role: .cancel, action: { })
.buttonStyle(BorderedButtonStyle())
.tint(.yellow)
// with controlSize
Button("Cancel", role: .cancel, action: { })
.buttonStyle(.borderless)
.controlSize(.small)
.tint(.yellow)
Button("Delete", role: .destructive, action: { })
.buttonStyle(.bordered)
.controlSize(.regular)
// with controlProminence
Button(role: .destructive, action: { }, label: {
Text("Exit").frame(maxWidth: .infinity)
})
.buttonStyle(.bordered)
.controlSize(.large)
.controlProminence(.increased)
//with BorderedShape
Button(role: .destructive, action: { }, label: {
Text("Wow shape").frame(maxWidth: .infinity)
})
.buttonStyle(BorderedButtonStyle(shape: .capsule))
.controlSize(.large)
.controlProminence(.increased)
.tint(.purple)
}
For Button:
Button("Tap") {
// do something
}
.foregroundColor(.red)
For Alert:
Alert(
title: Text("Hi"),
message: Text("Do it?"),
primaryButton: .cancel(Text("Cancel")),
secondaryButton: .destructive(Text("Delete")) {
// do something
}
)
And similarly for ActionSheet.
I have a modal window in Angular 4 that works fine but if the user clicks on the background / parent page the modal is closed.
I have found some solutions that suggest using backdrop='static' and keyboard=false when opening the modal but our modal uses a local Dialog class with a BehaviorSubject object so is opened using the .next method. I've also tried setting these attributes using div config but to no avail.
Therefore I'm looking for another solution, maybe using CSS or another setting / attribute that can be directly applied to the parent page or modal HTML.
See below for some of the relevant code.
dialog.component.ts:
constructor(private location: PlatformLocation,
private _dialog: DialogService,
private router: Router) { }
open() {
this.showDialog = true;
const body = document.body;
body.classList.add('cell-modal-open');
}
close() {
this.dialog = undefined;
}
private handleDialog(d: Dialog) {
if (!d) {
this.close();
} else if (d.template) {
if (this.showDialog) {
this.close();
}
this.dialog = d;
this.open();
}
}
ngOnInit() {
this.subscription = this
._dialog
.getDialog()
.subscribe({
next: (d) => { this.handleDialog(d); console.log('subscribed dialog') },
error: (err) => this.handleDialogError(err)
});
this.initialiseRoutingEventListeners();
}
dialog.service.ts
private d: Dialog = { template: null, size: DialogSizeEnum.XLarge };
private dialogSubject = new BehaviorSubject<Dialog>({ template: null, size: DialogSizeEnum.XLarge });
constructor() { }
showDialog(template: TemplateRef<any>, size = DialogSizeEnum.XLarge, requiresAction = false) {
Object.assign(this.d, { template: template, size: size, requiresAction: requiresAction });
if (this.d !== null) {
this.dialogSubject.next(this.d);
}
}
getDialog(): BehaviorSubject<Dialog> {
return this.dialogSubject;
}
clear() {
this.dialogSubject.next(null);
}
Any suggested approaches are welcome!
Added flag to the close() method and adding condition to only set to undefined if true (i.e. from a valid location).
I want to alter how an asyncCommand is being hit (currently from a button), so I would need to access the asyncCommand from code. I don't want to have to alter what this asyncCommand is doing, it is dealing with payment details.
I have tried Googling but I cant find anything, I am also new to KO.
This is what I'm trying to achieve:
Click on a button (a separate button with its own asyncCommand method
which checks a flag) The 'execute' will do the following:
If (flag) - show modal
modal has two options - Continue / Cancel
If continue - hit asyncCommand command for original button (card payment one).
If cancel - go back to form
If (!flag)
Hit asyncCommand command for original button (card payment one).
Can this be done?
Thanks in advance for any help.
Clare
This is what I have tried:
FIRST BUTTON
model.checkAddress = ko.asyncCommand({
execute: function (complete)
{
makePayment.execute();
if (data.shippingOutOfArea === true || (data.shippingOutOfArea === null && data.billingOutOfArea === true)) {
model.OutOfArea.show(true);
}
complete();
},
canExecute: function (isExecuting) {
return !isExecuting;
}
});
ORIGINAL BUTTON
model.makePayment = ko.asyncCommand({
execute: function (complete) {
}})
MODAL
model.OutOfArea = {
header: ko.observable("Out of area"),
template: "modalOutOfArea",
closeLabel: "Close",
primaryLabel: "Continue",
cancelLabel: "Change Address",
show: ko.observable(false), /* Set to true to show initially */
sending: ko.observable(false),
onClose: function ()
{
model.EditEmailModel.show(false);
},
onAction: function () {
makePayment.execute();
},
onCancel: function ()
{
model.EditEmailModel.show(false);
}
};
You will have two async commands actually for this scenario. One to open up the modal and another one for the modal.
Eg:
showPaymentPromptCmd = ko.asyncCommand({
execute: function(complete) {
if (modalRequired) {
showModal();
} else {
makePayement();
}
complete();
},
canExecute: function(isExecuting) {
return !isExecuting;
}
});
//Called by Continue button on your modal.
makePaymentCmd = ko.asyncCommand({
execute: function(complete) {
makePayement();
complete();
},
canExecute: function(isExecuting) {
return !isExecuting;
}
});
var
function makePayement() {
//some logic
}