Im used similar gesture recognizer on Image and it worked. But on a
map nothing work. What could be the reason? Im testing it on Droid project.
public class MapPage : ContentPage
{
Map map;
public MapPage()
{
map = new ExtendedMap
{
IsShowingUser = true,
HeightRequest = 100,
WidthRequest = 940,
VerticalOptions = LayoutOptions.FillAndExpand
};
map.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = new Command(()=> { OnAlertYesNoClicked(null, null); }),
NumberOfTapsRequired = 1
});
ContentLayout.Children.Add(map);
}
void NavClicked(object sender, EventArgs e)
{
IsShowRightPanel = !IsShowRightPanel;
}
async void OnAlertYesNoClicked(object sender, EventArgs e)
{
var answer = await DisplayAlert("Question?", "Would you like to play a game", "Yes", "No");
}
}
Not sure why it is not fired, but I can imagine it's not working because the map itself catches tap events etc.
I see you are using the ExtendedMap, have a look at TKCustomMap instead. It has a property for a Command to invoke code when the map is tapped.
Related
I am using SkiaSharp library to draw on canvas .
and I need to set a tap recognizer to a specific function when I double tap .
and the touch event of the canvas to do another functions .
each one works well separately but when I use them both , the Touch event cancels the Tap Recognizer .
is there is any way to use them both ?
<skia:SKCanvasView x:Name="canvasView"
PaintSurface="canvasView_paintSurface"
VerticalOptions="FillAndExpand"
EnableTouchEvents="true"
Touch="OnTouch">
<skia:SKCanvasView.GestureRecognizers >
<TapGestureRecognizer NumberOfTapsRequired="2" Tapped="OnTapped" >
</TapGestureRecognizer>
</skia:SKCanvasView.GestureRecognizers>
</skia:SKCanvasView>
private void OnTapped(object sender, EventArgs e)
{
DisplayAlert("hello", "OnTapped", "Ok", "Cancel");
}
private async void OnTouch(object sender, SKTouchEventArgs e)
{
DisplayAlert("hello", "OnTouch", "Ok", "Cancel");
}
One way of solving this is by creating two tap gesture recognizers and a timer to check the succession of the taps. This way you know whether there was a touch or a double tap
private bool tapHandled;
public XYZPage() : base()
{
var tgr = new TapGestureRecognizer();
tgr.NumberOfTapsRequired = 1;
tgr.Tapped += tapped;
GestureRecognizers.Add(tgr);
var ttgr = new TapGestureRecognizer();
ttgr.NumberOfTapsRequired = 2;
ttgr.Tapped += doubletapped;
GestureRecognizers.Add(ttgr);
}
private void tapped(object sender, EventArgs e)
{
tapHandled = false;
Xamarin.Forms.Device.StartTimer(new TimeSpan(0, 0, 0, 0, 300), taptimer);
}
private void doubletapped(object sender, EventArgs e)
{
tapHandled = true;
// do double tap work here
DisplayAlert("hello", "OnDoubleTapped", "Ok", "Cancel");
}
private bool taptimer()
{
if (!tapHandled)
{
tapHandled = true;
// do Touch stuff here
DisplayAlert("hello", "OnTouch", "Ok", "Cancel");
}
return false;
}
I have a Xamarin.Forms app. I would like to remove/hide the back arrow in my navigation bars but keep the title. I was able to do it in iOS using the following code inside my NavigationPageRenderer:
UINavigationBar.Appearance.BackIndicatorImage = new UIImage();
UINavigationBar.Appearance.BackIndicatorTransitionMaskImage = new UIImage();
Is there any equivalent code for this in Android that I could use inside my renderer or in the MainActivity? I tried this.ActionBar.SetDisplayHomeAsUpEnabled(false); inside my MainActivity but the ActionBar always returns null. Below is my my MainActivity code:
public class MainActivity : FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
if (Window != null)
{
Window.SetStatusBarColor(Android.Graphics.Color.Transparent);
}
this.ActionBar.SetDisplayHomeAsUpEnabled(false);
}
}
What I want my navigation bar to look like something like the image below (this is in my iOS app).
The back arrow is just the back button title: NavigationPage.SetBackButtonTitle(this, "\u25C3");
In my ContentPage:
public partial class HomeTabPage : ContentPage
{
public HomeTabViewModel vm;
public HomeTabPage()
{
InitializeComponent();
BindingContext = vm = new HomeTabViewModel(this);
NavigationPage.SetHasBackButton(this, false);
NavigationPage.SetBackButtonTitle(this, "\u25C3");
}
}
Solution:
You can define a custom view as navigationBar of your content on specific platform (Android).Refer to the following code.
public partial class Page1 : ContentPage
{
public Page1 ()
{
InitializeComponent ();
if(Device.RuntimePlatform=="Android")
{
NavigationPage.SetHasBackButton(this, false);
NavigationPage.SetTitleView(this, SetBackView("Title", "back"));
}
}
private void BackButton_Clicked(object sender, EventArgs e)
{
Navigation.PopAsync();
}
StackLayout SetBackView (string title,string backButtonContent)
{
Button backButton = new Button()
{
Text = backButtonContent,
TextColor = Color.White,
FontAttributes=FontAttributes.None,
BackgroundColor = Color.Transparent,
Margin = new Thickness(-20,0,0,0),
};
backButton.Clicked += BackButton_Clicked;
StackLayout stackLayout = new StackLayout
{
Children = {
backButton,
new Label{
HorizontalTextAlignment=TextAlignment.Center,
VerticalTextAlignment=TextAlignment.Center,
Text=title,
TextColor=Color.White,
BackgroundColor=Color.Transparent,
},
},
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.StartAndExpand,
Orientation = StackOrientation.Horizontal,
};
return stackLayout;
}
}
And the effect is just like the following ,you can set the content of page title and backButton as you want.
I want to open a UIViewController from a content page and this code helps me in doing this
UIWindow Window = new UIWindow(UIScreen.MainScreen.Bounds);
var cvc = new ScanningPage();
var navController = new UINavigationController(cvc);
Window.RootViewController = navController;
Window.MakeKeyAndVisible();
but I also want a Back button on that Controller Page which navigate me back to that content Page.
this.NavigationController.PopViewController(true);
or
this.DismissViewController(true,null);
not working in this case.
I would recommend not creating a new Window and a UINavigationController...
Xamarin.Forms is contained in a single VC in the first Window, so you can obtain that view controller via:
UIApplication.SharedApplication.Windows[0].RootViewController;
So as an example Dependency service that presents and dismisses a VC (either from Forms or the view controller), you can do something like this.
Dependency interface:
public interface IDynamicVC
{
void Show();
void Dismiss();
}
iOS Dependency implementation
public class DynamicVC : IDynamicVC
{
UIViewController vc;
public void Show()
{
if (vc != null) throw new Exception("DynamicVC already showing");
vc = new UIViewController();
var button = new UIButton(new CGRect(100, 100, 200, 200));
button.SetTitle("Back", UIControlState.Normal);
button.BackgroundColor = UIColor.Red;
button.TouchUpInside += (object sender, EventArgs e) =>
{
Dismiss();
};
vc.Add(button);
var rootVC = UIApplication.SharedApplication.Windows[0].RootViewController;
rootVC.PresentViewController(vc, true, () => { });
}
public void Dismiss()
{
vc?.DismissViewController(true, () =>
{
vc.Dispose();
vc = null;
});
}
}
I have a problem with RefreshControl... I have this code:
In ViewDidLoad() I call method InitializeRefreshControl();
private void InitializeRefreshControl()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(6, 0))
{
//UIRefreshControl iOS6
ordersCollectionView.RefreshControl = new UIRefreshControl();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Pull To Refresh",
new UIStringAttributes()
{
ForegroundColor = UIColor.Red,
KerningAdjustment = 3
});
ordersCollectionView.RefreshControl.ValueChanged += HandleValueChanged;
}
else
{
// old style refresh button and no PassKit for older iOS
NavigationItem.SetRightBarButtonItem(new UIBarButtonItem(UIBarButtonSystemItem.Refresh), false);
NavigationItem.RightBarButtonItem.Clicked += (sender, e) => { Refresh(); };
}
}
HandleValueChange method and Refresh merhod is here:
private void HandleValueChanged(object sender, EventArgs e)
{
ordersCollectionView.RefreshControl.BeginRefreshing();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Refreshing",
new UIStringAttributes()
{
ForegroundColor = UIColor.Blue,
KerningAdjustment = 5
});
Refresh();
ordersCollectionView.RefreshControl.EndRefreshing();
}
private void Refresh()
{
var viewModel = (OrdersViewModel)DataContext;
viewModel.OnReloadData();
}
My problem is when I pull down collectionVIew so Refresh loading is displayed but is stuck no loading effect and still with text "Pull to refresh". When method Refresh end so for 0,1ms is showing loading effect and text "Refreshing" but not before method Refresh... Someone know how solve this problem? Thanks for answer.
It looks like the issue is related to the Refresh(); method being synchronous. You'll need to make this operation happen in the background so that the UI thread is free to provide the animation for the RefreshControl. For example:
private async void HandleValueChanged(object sender, EventArgs e)
{
ordersCollectionView.RefreshControl.BeginRefreshing();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Refreshing",
new UIStringAttributes()
{
ForegroundColor = UIColor.Blue,
KerningAdjustment = 5
});
// await a Task so that operation is done in the background
await Refresh();
ordersCollectionView.RefreshControl.EndRefreshing();
}
// Marked async and Task returning
private async Task Refresh()
{
var viewModel = (OrdersViewModel)DataContext;
// Need to update this method to be a Task returning, async method.
await viewModel.OnReloadData();
}
The above code refactors what you had to use async/await and Tasks. You may need to refactor some more of your code to make that work, including the OnReloadData() method.
There are lots of resources for getting started with Tasks, async and await. I can start you off with this reference from the Xamarin blog.
In my application I have a Contentpage with a Scrollview in it. The Scrollview in turn contains a StackLayout with lots of labels with text. In the middle of this I want to insert some static HTML.
I have tried adding a Webview with static HTML as a child to the StackLayout but the Webview is not visible then (Probably the height gets calculated to zero)
Does anyone know how to fix this? Or is there some other way to add HTML in the between all the labels?
StackLayout mainLayout = new StackLayout ();
// Adding many labels of different sizes
mainLayout.Children.Add (new Label {Text = "Label text"});
mainLayout.Children.Add ( new WebView {
Source = new HtmlWebViewSource {
Html = "<html><body>Hello</body></html>"
}
});
// Adding many more labels of different sizes
mainLayout.Children.Add (new Label {Text = "Even more Label text"});
Content = new ScrollView {
Content = mainLayout
};
See https://developer.xamarin.com/guides/xamarin-forms/user-interface/webview/, in particular:
WebView requires that HeightRequest and WidthRequest are specified
when contained in StackLayout or RelativeLayout. If you fail to
specify those properties, the WebView will not render.
The HeightRequest and WidthRequest are not set in the code snippet above, so fixing this should be the next thing to try.
I found a great solution here
using Xamarin.Forms;
namespace YourNamespace
{
public class HtmlLabel : Label
{
}
}
iOS renderer
[assembly: ExportRenderer(typeof(HtmlLabel), typeof(HtmlLabelRenderer))]
namespace YourNamespace
{
class HtmlLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
var attr = new NSAttributedStringDocumentAttributes();
var nsError = new NSError();
attr.DocumentType = NSDocumentType.HTML;
var text = e.NewElement.Text;
//I wrap the text here with the default font and size
text = "<style>body{font-family: '" + this.Control.Font.Name + "'; font-size:" + this.Control.Font.PointSize + "px;}</style>" + text;
var myHtmlData = NSData.FromString(text, NSStringEncoding.Unicode);
this.Control.AttributedText = new NSAttributedString(myHtmlData, attr, ref nsError);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (this.Control == null)
{
return;
}
if (e.PropertyName == Label.TextProperty.PropertyName)
{
var attr = new NSAttributedStringDocumentAttributes();
var nsError = new NSError();
attr.DocumentType = NSDocumentType.HTML;
var myHtmlData = NSData.FromString(this.Control.Text, NSStringEncoding.Unicode);
this.Control.AttributedText = new NSAttributedString(myHtmlData, attr, ref nsError);
}
}
}
}
Android renderer
[assembly: ExportRenderer(typeof(HtmlLabel), typeof(HtmlLabelRenderer))]
namespace YourNamespace
{
class HtmlLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
Control?.SetText(Html.FromHtml(Element.Text), TextView.BufferType.Spannable);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Label.TextProperty.PropertyName)
{
Control?.SetText(Html.FromHtml(Element.Text), TextView.BufferType.Spannable);
}
}
}
}
I found Html.FromHtmlis pretty basic. I ended up binding this library which helped with lists and (with a bit of work) span tags.