How to make an action connection for multiple buttons in xcode10? - button

I am wondering how do we make an action connection for multiple buttons in Xcode10. Just ONE CODE for ALL buttons.NOT SEPARATE.
For example, we have 7 music key buttons, and we want to make one action connection for all 7 of them, and then continue to code to make the code know which key is being exactly pressed. As the code that I posted in this question, how do we make " #IBAction func notePressed(_ sender: UIButton)" this line? Not by typing, by clicking and dragging.
Another example is, we have "true" and "false", two buttons. We want to make just one action connection for these two buttons in ViewController.swift.
I know how to make one action connection for a single button, but when it comes to multiple buttons, I do not know how to do. I use xcode10
I have tried to hold the command and select all the buttons at the same time, and then hold the control key and try to drag all the buttons from the mainStoryBoard to my swift code, but it seems that I only created an action connection for on button in this way.
#IBAction func notePressed(_ sender: UIButton)
{
let selectedNote : String = soundArray[sender.tag - 1]
playSound(inputNote : selectedNote)
}

If you find the connected actions part in the First Responder part in the storyboard, you are able to connect it one by one as the screenshot.

Related

fragment vs activity onClickButton functions - where is right place

I made an andorid application using the "activity" component. I used it for a while, but over time I found that one page was not enough for me. I needed to add tabLayout, viewPager2 and three fragments.
I needed to migrate the functionality from mainActivity to the firstFragment.
I encountered a problem with events during the XML migration. In the activity I called android: onClick = "myOnClicFunction". But is that not possible?
I know that this has been discussed several times here in several threads. However, I would like to ask how to proceed correctly.
Here in the thread Android Fragment onClick button Method is the advice "even if you are in the fragment, put the onClick functions into activity" there is another similar advice to, "if you insist to make it in a fragment"
But what is right? Should the onclick function be correct in the activity or in the fragment?
I don't want to put the source code here, because I don't know which procedure is correct at all.
I am creating a program that is for testing students. An example is displayed and a response is pending.
The first tab has a lot of buttons + saves the result to an xml files.
The second tab will contain statistics. From saved files displays how many calculations were correct, wrong, average response time, etc ...
In Activity I had functions like getQuestion(), evaluateQuestion(), saveToXml() and events zeroWasPressed(), oneWasPressed(), twoWasPressed(), threeWasPressed() ...
Where should I put all these functions when I change the application from Activity to Fragment?
on click function listener should be placed in corresponding fragment, where the button is added in fragment layout.
events zeroWasPressed(), oneWasPressed(), twoWasPressed(), threeWasPressed() ... should be placed inside corresponding fragment where those buttons were added in layout.
getQuestion(), evaluateQuestion(), saveToXml() , this function can be placed in your activity or some other custom classes, that would appropriate according to architecture viewpoint.

Send Data from Fragment to Main Activity through NavGraph

In my app, I'm using a Single Activity with multiple Fragments Architecture and I navigate between them using the Navigation Library. Within one of the fragments, I have multiple categories, each associated with an ID. When a category is clicked, I take the user to that respective category explainer screen with the code below.
val directions = MainNavGraphDirections.launchFragmentWithThisCategoryId(categoryId!!)
onRoute(AppRoute.Directions(directions))
The above code sends them to the explainer screen associated with the associated categoryId. All is well until this point, the right explainer screen gets launched based on the categoryId. Within this explainer screen, I have a deep-link with a tag chatbot://fragment/wizardintro that is supposed to let the main activity know the specific follow-up fragment to send the user to. I denote all the fragments that can receive this deep-link with the code below.
companion object{
const val DEEP_LINK = "chatbot://fragment/wizardintro"
}
In the MainActivity, I have a method that receives all the different deep linking intents and matches them to the tags that will launch the respective category fragment with the code below.
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.data?.toString().also { deepLink ->
when (deepLink) {
IntroductionFragment.DEEP_LINK ->{
val categoryId = intent?.getLongExtra("categoryId", 0L)
val directions = MainNavGraphDirections.actionGlobalGoalWizard(categoryId)
navController.popBackStack()
navController.navigate(directions)
}
}
Now my problem arises when I try to retrieve this categoryId in the Main Activity and pass it to the next fragment. I don't get anything and only the default Long gets passed along. I think the function override fun onNewIntent(intent: Intent?) { } in the MainActivity recieve any intent. To be clear, these intents are sent from the explainer fragment which is technically a fragment that loads a json. Within the json there is a "route": {"type": "route", "url": "chatbot:///fragment/wizardintro"
In the MainActivity, the onNewIntent functions receive these intents unpacks them with this line intent?.data?.toString().also ...then in the when statement picks a fragment that has a matching chatbot:///fragment/wizardintro
I said all this to say that the main activity doesn't actually gets the categoryId, it simply picks launching the neccessary fragment without actually having anything associated with the categoryId
This makes me think that the first clicked categoryId doesn't actually get passed to the MainActivity. Although, to me, this seems like it shouldn't be this hard to pass objects/data from a fragment to an activity. What am I missing? What can I read to educate myself more on this?
Thanks for your time and responses!
Since we already got to the conclusion that MainActivity is not getting categoryId, you just need to pass that categoryId with the deep-link.
However, there is no need for any communication from Fragment to Activity.
You could achieve the same result through communication only between Fragment to Fragment, and Activity to Fragment.
What you want to do is to look more closely on deep-links and android navigation in the AndroidDocs, click here.
As you can tell, there are different ways to go around this, starting with arguments for each fragment. Assigning categoryId as an argument to the Fragment would help you use the navigationController and navigate to the new Fragment, while passing the categoryId to it.
Now, I am aware that you also wish to launch it with a deep-link; there's also a good explanation on here. According to the docs, you can place arguments in deep-links in the following manner...
Placeholders in the form of {placeholder_name} match one or more characters. For example, http://www.example.com/users/{id} matches http://www.example.com/users/4. The Navigation component attempts to parse the placeholder values into appropriate types by matching placeholder names to the defined arguments that are defined for the deep link destination. If no argument with the same name is defined, a default String type is used for the argument value.
The navigation is something amazing and capable, you just need to be aware of everything it can actually do. You can even bind it to a BottomNavBar, with extremely minimal amount of code.
Try going over the AndroidDocs about it, and it'll grow on you for sure.

How do I dismiss a WKInterfaceController that was presented with contextForSegue?

I have a WKInterfaceController with a WKInterfaceTable that lists some events a user has recorded in my app.
A user can tap a row of the table to see more details about that event. To accomplish this, I've overridden contextForSegue(withIdentifier:in:rowIndex:) on the WKInterfaceController that contains the table, so tapping a row modally presents a detail view of that row in a new WKInterfaceController called EventDetailController.
The modal presentation is defined on the Storyboard. I can't use push presentation because the WKInterfaceController with the WKInterfaceTable is a page among multiple instances of WKInterfaceController at the top level of my app.
Here's the main issue:
Within the EventDetailController, there's a Delete button to destroy the record that the table row represents.
When a user taps the Delete button, I present an alert that allows the user to confirm or cancel the delete action.
Once the user confirms the record deletion, I want to dismiss the EventDetailController since it's no longer relevant, because it represents a deleted record.
Here's the IBAction defined on EventDetailController that gets called when the Delete button is tapped:
#IBAction func deleteButtonTapped(_ sender: WKInterfaceButton) {
let deleteAction = WKAlertAction(title: "Delete", style: .destructive) {
// delete the record
// as long as the delete was successful, dismiss the detail view
self.dismiss()
}
let cancelAction = WKAlertAction(title: "Cancel", style: .cancel) {
// do nothing
}
presentAlert(withTitle: "Delete Event",
message: "Are you sure you want to delete this event?",
preferredStyle: .alert,
actions: [deleteAction, cancelAction])
}
The problem is that watchOS doesn't seem to allow this. When testing this code, the EventDetailController does not dismiss. Instead, an error message is logged in the console:
[WKInterfaceController dismissController]:434: calling dismissController from a WKAlertAction's handler is not valid. Called on <Watch_Extension.EventDetailController: 0x7d1cdb90>. Ignoring
I've tried some weird workarounds to try to trick the EventDetailController into dismissing, like firing a notification when the event is deleted and dismissing the EventDetailController from a function that's called from an observer of the notification, but that doesn't work either.
At this point I'm thinking there's some correct way I'm supposed to be able to dismiss a WKInterfaceController, or in other words reverse the contextForSegue(withIdentifier:in:rowIndex:) call, but I don't know what it is.
When I call dismiss() directly in the IBAction, instead of in a WKAlertAction handler, it works fine, but I don't like this implementation since it doesn't allow the user to confirm the action first.
I feel like an idiot, but I figured out the solution.
The answer was in Apple's WKInterfaceController.dismiss() documentation the whole time (emphasis added):
Call this method when you want to dismiss an interface controller that you presented modally. Always call this method from your WatchKit extension’s main thread.
All I had to do differently was call self.dismiss() on the main thread.
Here's my updated code for the delete action, which now works as expected:
let deleteAction = WKAlertAction(title: "Delete", style: .destructive) {
// delete the record
// as long as the delete was successful, dismiss the detail view
DispatchQueue.main.async {
self.dismiss()
}
}
Hopefully this will save someone else some troubleshooting time!

QT Creator: Trigger a Slot with Code?

I may have worked myself into a corner but this sounded to me like a good idea at the time.
I have been developing an interface that permits a user to modify settings of a robotic device, i.e. speed, directions, force, etc. with a very large series of options in the form of ComboBoxes. The problem is that there are about a thousand of these things, in sub categories. e.g. Speed category x1, x2, x3, Y1, y2, etc. So rather than create a thousand comboboxes in QT, I thought the good idea was to create one set of 50 (ish) and then provide a few button to switch between categories. So when the user selects speed QT, populates the comboboxes with the appropriate options, sets the style sheets and text for the labels etc. So it appears as though a dedicated page exists. Then if the user selects Direction, QT Writes the current index of each box to a dedicated array and then repopulates the boxes, labels etc with the appropriate content. I then do this over and over for the various needs of the system.
All of that seems to work fine. However I am now in a bind where the options provided to navigate to each page have grown. For instance I have forward / backward buttons (like you woudl expect in a set-up wizard), as well as action menus at the top to jump to a page. So now the code is becoming very repetitious. If you select the next button, I write the current values to array, then repopulate. If you jump to the page from anywhere, I look to see where I am, write it to array, and populate the boxes. Thus if I need to change anything I have to make the change in numerous places in the code.
I know that this is not optimal. What I woudl like to do is run a continuous loop as I woudl normally do with Micros in C. So the program can look at a variable in each pass and if it is then it does. I am not however skilled enough to figure this loop out in QT. So my new thought was...
Is it possible to trigger an action or slot with a variable. For example, if the user presses the Next button it triggers a slot for a button that does not exist, so that QT will execute a particular line of Code? Then I can have 1 dedicated section focused on reading and writing boxes, with a bunch of actions that will take me there.
You can make a signal that is triggered with an emit call in your code, so you'd hook up the next button signal of clicked to a slot that does some work and moves on, or directly calls another signal that you've created that triggers a slot elsewhere, or do some work in a lambda triggered by the button press.
I would first load all the ComboBoxes options in a QStringList array (or maybe an array of QList<QLatin1String> lists - for memory saving and code efficiency).
Then I would keep an array of a 1000 integers for current ComboBox indexes.
When the user changes a value in some ComboBox, the currentIndexChanged signal will trigger the corresponding slot (a single slot for all the ComboBoxes would be enough - sender()->objectName() to get the name of the ComboBox which had sent the signal):
void WindowWidget::on_ComboBox_currentIndexChanged(int index)
{
name = sender()->objectName();
/* here change the corresponding integer in the current
indexes array */
}
On Next/Back button push repopulate the ComboBoxes. Also, provide some 'Save' button for saving the ComboBoxes indexes (or trigger the Save slot on some action, i.e. on window close either even on a timer signal).

Asynchronous validation in QWizard

I'm writing a wizard UI based on the QWizard Qt object. There's one particular situation where I want the user to log in to a service using host, username, and password. The rest of the wizard then manipulates this service to do various setup tasks. The login may take a while, especially in error cases where the DNS name takes a long time to resolve -- or perhaps it may not even resolve at all.
So my idea is to make all three fields mandatory using the registerField mechanism, and when the user hits Next, we show a little throbber on the wizard page saying "Connecting to server, please wait..." while we try to connect in the background. If the connection succeeds, we advance to the next page. If not, we highlight the offending field and ask the user to try again.
However, I'm at a loss for how to accomplish this. The options I've thought of:
1) Override validatePage and have it start a thread in the background. Enter a wait inside validatePage() that pumps the Qt event loop until the thread finishes. You'd think this was the ugliest solution, but...
2) Hide the real Next button and add a custom Next button that, when clicked, dispatches my long running function in a thread and waits for a 'validation complete' signal to be raised by something. When that happens, we manually call QWizard::next() (and we completely bypass the real validation logic from validatePage and friends.) This is even uglier, but moves the ugliness to a different level that may make development easier.
Surely there's a better way?
It's not as visually appealing, but you could add a connecting page, and move to that page. If the connection succeeds, call next() on the wizard, and if the connection fails, call previous() and highlight the appropriate fields. It has the advantage of being relatively straightforward to code.
My final choice was #2 (override the Next button, simulate its behavior, but capture its click events manually and do the things I want to with it.) Writing the glue to define the Next button's behavior was minimal, and I was able to subclass QWizardPage with a number of hooks that let me run my task ON the same page, instead of having to switch to an interstitial page and worry about whether to go forwards or backwards. Thanks Caleb for your answer though.
I know this has already been answered (a long time ago!) but in case anyone else is having the same challenge. Another method for this is to create a QLineEdit, initiate it as empty and set it as a mandatory registered field. This will mean that "Next" is not enabled until it is filled with some text.
Run your connection task as normal and when it completes use setText to update the QLineEdit to "True" or "Logged in" or anything other than empty. This will then mean the built in isComplete function will be passed as this previously missing mandatory field is now complete. If you never add it to the layout then it won't be seen and the user won't be able to interact with it.
As an example ...
self.validated_field = QLineEdit("")
self.registerField('validated*', self.validated_field)
and then when your login process completes successfully
self.validated_field.setText("True")
This should do it and is very lightweight. Be sure though that you consider the scenario where a user then goes back to that page and whether you need to reset the field to blank. If that's the case then just add in the initialisePage() function to set it back to blank
self.validated_field.setText("")
Thinking about it you could also add the line edit to the display and disable it so that a user cannot update it and then give it a meaningful completion message to act as a status update...
self.validated_field = QLineEdit("")
self.validated_field.setDisabled(True)
self.validated_field.setStyleSheet("border:0;background-color:none")
self.main_layout.addWidget(self.validated_field)
self.registerField('validated*', self.validated_field)
and then when you update it..
self.validated_field.setText("Logged in")

Resources