Till iOS12, I was using following code to handle tap event on status bar:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let statusBarRect = UIApplication.shared.statusBarFrame
guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }
if statusBarRect.contains(touchPoint) {
//Status bar tapped
}
}
}
But, now it's not working for iOS 13. Please help me on the same.
A view in your app that's behind the status bar cannot be touched. You should not be trying, in your app, to detect a tap "on the status bar"; it isn't even part of your app (it's a window imposed by the runtime). It can't be done.
Related
I am building a Xamarin Forms mobile app that runs in Android on a Zebra scanner. I flip 2 different StackLayouts to IsVisble true/false to display different stuff in the UI. (StackLayout1 and StackLayout2)
The customer wants the user to be able to use the app entirely from the hardware keyboard on the scanner. So I have used the device Settings so that it never displays the virtual keyboard (I don’t think that matters for the issue I am having.)
I am overriding DispatchKeyEvent in a PageRenderer in the Android project and everything is working great … except.
The problem case:
StackLayout1 is displayed
the user taps an Entry control, putting the focus there
the user taps a button in the UI
the app displays StackLayout2
at this point the DispatchKeyEvent never fires no matter what key I press on the device keyboard
If an Entry box does NOT get the focus (step #2 above) the DispatchKeyEvent always fires in StackLayout2 and the StackLayouts display as expected.
If I programatically put the focus in an Entry box in StackLayout2 at step #3 above the DispatchKeyEvent fires fine.
That is not an OK solution. I have tried to progamatically put the focus on StackLayout2, and that code seems to do what is expected but DispatchKeyEvent does not fire.
Maybe I need to do something in the Android-project PageRenderer so that it is aware of StackLayout2 when it is made IsVisible = true.
Update 2: I found that I did NOT need custom StackLayouts. The solution which I posted below does not include any of this stuff I am describing in Update 1 (sorry, if that's confusing).
Update 1:
I added a ViewRenderer for both StackLayouts, and the code is hitting the OnElementChanged event when StackLayout2's IsVisible property flips to true, just great. Although the problem case is the same: DispatchKeyEvent does not fire once StackLayout2 is displayed, if an EntryBox had the focus in StackLayout1
Here is the OnElementChanged part of the new StackLayout ViewRenders
async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "IsVisible":
if (Element.IsVisible)
{
if (sender is StackLayout)
{
this.FocusableViewAvailable(this); // if I comment these 2 lines out I get the same bad result
this.Focusable = true; // if I comment these 2 lines out I get the same bad result
this.FocusableInTouchMode = true;
var dd = this.RequestFocus(); // this is always false
var ee = this.IsFocused; // this is always false
}
}
break;
}
}
Also, as I am pointing out in the comments ^ there, IsFocused is always false.
Ideas?
My hunch, "Maybe I need to do something in the Android project PageRenderer" was correct. In the DispatchKeyEvent I had to make the MainPage have the focus when the keypress was handled.
Here is what the DispatchKeyEvent looks like now (notice the comments):
public override bool DispatchKeyEvent(KeyEvent ke)
{
// MainPage.ReceiveKeyPress(e); is the method that this method returns to
bool KeyPressWasHandled = false;
KeyPressWasHandled = (Element as MainPage).ReceiveKeyPress(ke);
if (KeyPressWasHandled)
{
// this next block seems to be needed so that this class
// continues to receive the keypress event after an Entry box has had the focus
this.Focusable = true;
this.FocusableInTouchMode = true;
this.RequestFocus();
return true; // returning true tells the parent class that the keypress has been handled
} else
{
try
{
return base.DispatchKeyEvent(ke);
}
Now the "problem case" in my initial post is no longer a problem.
NOTE: I found that I did NOT need the custom ViewRenderers that I had made for the StackLayouts.
I'm writing an app using Xamarin Forms and I have an issue I was hoping someone can help with.
My app contains a screen which has multiple icons that can be pressed which would then open a new screen.
My issue is that if you press the icon twice really fast, the app opens up 2 instances of the same screen (it's not just related to a double press, if you press the icon 6 times very fast it will open up 6 duplicate screens). Pressing the Back button, closes the top screen to reveal the duplicate screen underneath. Pressing the Back button again navigates you back to the original screen.
This issue seems to occur on any screen within my app so I'm hoping other people will have experienced it and know of a solution to prevent duplicate screens being displayed.
This is a known issue in TapEvents.
My hack is, in the code-behind, have a bool variable _canTap.
Inside the method you are calling to push new page, first you check if canTap, then set to false, and only set to true after navigating to another page. This way all taps will be disregarded.
Example:
private bool _canTap = true;
public void YourMethod()
{
if(_canTap)
{
_canTap = false;
YourMethodToNavigate();
_canTap = true;
}
}
In the Icon_Pressed method add this,
this.IsEnabled = false;
await Navigation.PushAsync(new MyPage());
this.IsEnabled = true;
It disables the page until the current Icon pressed event is finished
This is known problem with Xamarin apps. I've used a private variable combined with a try-finally pattern to solve this. Ex:
bool allowTap = true;
public void ButtonTapped()
{
try
{
if(allowTap)
{
allowTap = false;
// Do whatever...
}
}
finally
{
allowTap = true;
}
}
The finally makes sure allowTap gets set back to true no matter what happens, short of a complete crash. Note that you can also use a catch block between the try and finally blocks to grab any errors if needed.
I have manually/programmatically set up an up button in my toolbar for a fragment page with the following code in onCreateOptionsMenu in the fragment:
(activity as AppCompatActivity).setSupportActionBar?.setDisplayHomeAsUpEnabled(true)
Tapping the system back button will take the user back to the previous fragment but without an up button (that works) I think some users may get lost.
I am having difficulty trying to work out how to catch and handle the up button to pop the fragment off the back stack.
This SO post shows how to catch the the up button click however it is in Java and doesn't go on to explain how you would navigate up.
I think it needs to look something like the code below but there are errors everywhere:
The case android.R.id.home is showing an 'Incompatible types:Int and MenuItem' error?
onBackPressed() is showing an 'Unresolved reference' error.
Bad code:
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item) {
android.R.id.home -> {
onBackPressed()
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
UPDATE:
A comment to another SO post has a potential solution in Kotlin. It catches the click on the up button and goes back to the previous fragment page but then the up button doesn't go away. So the up button now persists even on the top level fragment destinations in my app (the pages corresponding to each tab in the BottomNavigationView).
I think this might have to do with the fact that there is only one activity in my app and the way that I have set up the up button in the fragment as mentioned above? If so, is there a workaround or other way to set up the up button by referencing the fragment instead of the whole activity?
If it helps, this is the code in the RecyclerView inner ViewHolder class in the adapter.kt file that navigates to the fragment page in question:
class AdapterListItemDetails(val items: List<ItemsList>) : RecyclerView.Adapter<AdapterListItemDeatils.ItemsViewHolder>() {
//overrides for OnCreateViewHolder, getItemCount, onBindViewHolder
inner class ItemsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var currentItem: ItemsList? = null
var currentPosition: Int = 0
init {
itemView.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.goto_details, null))
}
fun setData(itemsList: ItemsList, position: Int) {
itemView.tview_Keys.text = itemsList!!.nameText
this.currentItem = itemsList
this.currentPosition = position
}
}
}
You have to override onBackPressed() method in activity and handle the fragment transactions with your manual code. If you could share some snippet of activity and fragment transactions will help me to give some proper solution.
Hi this is what i usually do:
in an activity find the navController from your navHostFragment
val navController = this.findNavController(R.id.myNavHostFragment)
Make sure it's connected to the ActionBar
NavigationUI.setupActionBarWithNavController(this, navController)
Then simply override onSupportNavigateUp, find your navController then navigate up
override fun onSupportNavigateUp(): Boolean{
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
My app receives toast from PHP using WNS server. Now I want to perform some actions when clicking on toast as listed below.
When the app is not active - the user should redirect to a page on the app "ShowPage".
When the app is active - the toast should show two buttons "Show" and "Cancel". When clicking on Show button app should redirect to "ShowPage"
My current toast from PHP is
$toastMessage= '<?xml version="1.0" encoding="utf-8"?>'.
'<toast launch="">'.
'<visual baseUri="">'.
'<binding template="ToastGeneric">'.
'<text>'.$subtitle.'</text>'.
'</binding>'.
'</visual>'.
'<actions />'.
'</toast>';
And I'm calling below function on App.xaml.cs
private async void RegisterEngagementNotification()
{
StoreServicesEngagementManager engagementManager = StoreServicesEngagementManager.GetDefault();
await engagementManager.RegisterNotificationChannelAsync();
}
Please see the documentation for sending a local toast and handling activation. Everything applies there (other than you're sending the toast from your server, but otherwise adding buttons and handling activation remains the same).
I saw that you're using StoreServicesEngagementManager APIs, then I know you're sending toast notification from windows developer dashboard. So, if you want to your toast contains two buttons, you would need to add actions like the following:
Then, in your "App.xaml.cs" file, you would need to add some code to handle this option in OnActivated.
protected override void OnActivated(IActivatedEventArgs args)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
}
base.OnActivated(args);
var toastActivationArgs = args as ToastNotificationActivatedEventArgs;
if (toastActivationArgs.Argument =="ShowPage")
{
rootFrame.Navigate(typeof(ShowPage));
}
}
In a JavaFX stage, I want to validate user input when the focus leaves a textfield. If the user input is not a valid age (0 to 120), then a Dialog using ControlsFX Dialogs with an error message is displayed.
Here's the code:
participantAgeTextField.focusedProperty()
.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean oldPropertyValue, Boolean newPropertyValue)
{
if( !newPropertyValue ) { // lost focus
if( !participantAgeTextField.getText().isEmpty() ) {
if ( participantAgeTextField.getText().matches("^\\d+$")) {
int val = Integer.
parseInt(participantAgeTextField.getText());
if( val <= 0 ) {
val = 1;
} else if( val > 120 ) {
val = 120;
}
participantAgeTextField.setText(""+val);
} else {
participantAgeTextField.setText("");
Dialogs.create()
.owner(null)
.title("Error")
.masthead(null)
.message("You must enter a valid age!")
.showError();
}
}
}
}
});
This works fine, except when a user enters an invalid value, and then tries to close the window by clicking the "X" button in the top right corner of the window (stage).
In this case the application 'hangs'. (Strangely enough only in Windows, does not happen in Linux).
I have been looking for a fix, like not displaying the message when the focus changes to the window's "X". However I did not find a way to detect this.
Other ideas how to fix this would be greatly appreciated!
Joris
EDIT
Probably ControlsFX causes the crash. I cannot use JavaFX dialogs (introduced in 8u40) because I'm using Javafx 8u25. Any alternatives welcomed!
EDIT 2
The crash can be avoided by not using ControlsFX Dialogs but creating the error message 'by hand' as suggested by DVarga. But this causes the error message to show up after the window has been closed. Any ideas on how to prevent that from happening?
I think I will circumvent the crash as follows:
In stead of showing a "popup" dialog window after loss of focus in case of wrong input, I will display an error message next to the input box in red. On re-gaining focus I can then remove the error message.