Xamarin.Forms Warning: Attempt to present * on * whose view is not in the window hierarchy with iOS image/gesture recogniser - xamarin.ios

I have a modal Navigation page with an image which acts like a button;
<Image Source ="share.png" HeightRequest="32" WidthRequest="32">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="On_Share" />
</Image.GestureRecognizers>
</Image>
And the method behind;
async void On_Share(object sender, EventArgs e)
{
if (CrossConnectivity.Current.IsConnected)
{
var message = "Share this";
var title = "Share";
await CrossShare.Current.Share(new ShareMessage { Text = message, Title = title}, new ShareOptions { ExcludedUIActivityTypes = new[] { ShareUIActivityType.PostToFacebook } });
}
else
{
NoInternetLabel.IsVisible = true;
}
}
I'm getting the error when I try to click on the share image/button. I've put breakpoints into the first line of the On_Share method & they're not being hit.
Warning: Attempt to present <UIActivityViewController: 0x141b60f70> on <Xamarin_Forms_Platform_iOS_ModalWrapper: 0x1419a0920> whose view is not in the window hierarchy!
Please note this works fine in Android, I'm only seeing issues in iOS. I'm not sure what is going on - I'm not trying to present any other windows or anything when I click the image. Regardless, the error appears before the process reaches the beginning of the On_Share method. What am I missing here?
EDIT: The method does get hit now, and I'm still getting the error. It must be trying to send up the share sheet and failing...

There was a problem with the Share plugin in the end - we resolved it by making part of the code recursive.
the GetVisibleViewController used to look like this;
UIViewController GetVisibleViewController()
{
var rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;
if (rootController.PresentedViewController == null)
return rootController;
if (rootController.PresentedViewController is UINavigationController)
{
return ((UINavigationController)rootController.PresentedViewController).VisibleViewController;
}
if (rootController.PresentedViewController is UITabBarController)
{
return ((UITabBarController)rootController.PresentedViewController).SelectedViewController;
}
return rootController.PresentedViewController;
}
whereas it needed to cycle through to find the top UIViewController;
UIViewController GetVisibleViewController(UIViewController controller = null)
{
controller = controller ?? UIApplication.SharedApplication.KeyWindow.RootViewController;
if (controller.PresentedViewController == null)
return controller;
if (controller.PresentedViewController is UINavigationController)
{
return ((UINavigationController)controller.PresentedViewController).VisibleViewController;
}
if (controller.PresentedViewController is UITabBarController)
{
return ((UITabBarController)controller.PresentedViewController).SelectedViewController;
}
return GetVisibleViewController(controller.PresentedViewController);
}
I've raised the issue and submitted a pull request on the github

Related

Using Close Command in Method

I am trying to fire a command to close my page after finalizing the execution of a method. However, the Close() command is not working.
According to the code below, how do I close my view after finishing the method execution?
My Model:
public IMvxCommand BtnSaveCommand
{
get
{
return new MvxAsyncCommand(updateOrder);
}
}
private async Task<bool> updateOrder()
{
var errors = validator.Validate(this);
if (!errors.IsValid)
{
messageService.showMessage(errors);
return false;
}
var responseEdit = await orderService.update(configureOrder());
if (responseEdit == null)
{
messageService.showMessage("Pedido " + Item.id + " foi editado com sucesso.");
configureUpdateItem();
//Close View
Close(this);
}
else
{
messageService.showMessage((IErrorCollection)responseEdit);
}
return true;
}
--Update
Adding more information, when triggered the Close(this) command I get the following error:
Mvx: Warning: 325,38 Do not know how to close this viewmodel - topmost view does not present this viewmodel
You need to call DismissViewController() on the NavigationController

Unity Vuforia Google VR - Can't make onPointerEnter to GameObject change material for itself

I have two 3d buttons in my scene and when I gaze into any of the buttons it will invoke OnPointerEnter callback and saving the object the pointer gazed to.
Upon pressing Fire1 on the Gamepad I apply materials taken from Resources folder.
My problem started when I gazed into the second button, and pressing Fire1 button will awkwardly changed both buttons at the same time.
This is the script I attached to both of the buttons
using UnityEngine;
using UnityEngine.EventSystems;
using Vuforia;
using System.Collections;
public class TriggerMethods : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
Material _mat;
GameObject targetObject;
Renderer rend;
int i = 0;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Fire1"))
TukarMat();
}
public void OnPointerEnter(PointerEventData eventData)
{
targetObject = ExecuteEvents.GetEventHandler<IPointerEnterHandler>(eventData.pointerEnter);
}
public void OnPointerExit(PointerEventData eventData)
{
targetObject = null;
}
public void TukarMat()
{
Debug.Log("Value i = " + i);
if (i == 0)
{
ApplyTexture(i);
i++;
}
else if (i == 1)
{
ApplyTexture(i);
i++;
}
else if (i == 2)
{
ApplyTexture(i);
i = 0;
}
}
void ApplyTexture(int i)
{
rend = targetObject.GetComponent<Renderer>();
rend.enabled = true;
switch (i)
{
case 0:
_mat = Resources.Load("Balut", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 1:
_mat = Resources.Load("Khasiat", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 2:
_mat = Resources.Load("Alma", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
default:
break;
}
}
I sensed some logic error and tried making another class to only manage object the pointer gazed to but I was getting more confused.
Hope getting some helps
Thank you
TukarMat() is beeing called on both buttons when you press Fire1. If targetObject is really becoming null this should give an error on first button since it's trying to get component from a null object. Else, it'll change both as you said. Make sure OnPointerExit is beeing called.
Also, it seems you are changing the shared material.
The documentation suggests:
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
So, try changing the material property instead of sharedMaterial since it'll change the material for that object only.

Detect scrolling event on listview with winjs

I need to load more items as soon as the user scroll to the end of my list view.
I tried to use the microsoft sample : http://code.msdn.microsoft.com/windowsapps/ListView-loading-behaviors-718a4673/view/SourceCode (scenario 2) but it seams that list view have not the same behavior in windows phone 8.1.
When I run the sample I can see that only viewable contents are loaded (eg 5items of 50).
But for windows phone it does load all items.
I use this code :
listView.winControl.itemTemplate = this.incrementalTemplate;
incrementalTemplate: function (itemPromise, recycledElement) {
if (!recycledElement) {
recycledElement = document.createElement('div');
}
var renderComplete = itemPromise.then(function (item) {
console.log(item.index);
itemTemplate.winControl.render(item.data, recycledElement);
return item.ready;
}).done(function (item) {
console.log("clp"+item.index);
});
return { element: recycledElement, renderComplete: renderComplete };
},
Items are loaded asynchronusly. I can see in my console that it print 50 times the index and 50times the clp+index. Even if my list just show 5 items at a time.
Also it seems that my listview never fired the loading state event
listView.addEventListener("loadingstatechanged", function (args) {
//never fired
}, false);
The listview is in a hub, the solution was to add an onscroll event on the win pivot item:
document.querySelector(".win-pivot-item-content").onscroll = function () {
if (self.scrollAtBottom(this) === true) {
//load more
}
};
scrollAtBottom : function(element){
return element.scrollHeight - element.scrollTop === element.clientHeight
},
If you want to subscribe to event when the ListView was scrolled, you can take ListView's Scrollviewer and subscribe to ViewChanged event. The only problem is that I do not know how it would look like in winjs, in C# it can look like that:
// method to pull out a ScrollViewer
public static ScrollViewer GetScrollViewer(DependencyObject depObj)
{
if (depObj is ScrollViewer) return depObj as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = GetScrollViewer(child);
if (result != null) return result;
}
return null;
}
// subscription:
GetScrollViewer(yourListView).ViewChanged += yourEvent_ViewChanged;
Maybe it will help.
The comment voted here as solution does not work just like that out the box, however I found a solution to this problem starting from that.
See my first answer on the issue I opened at https://github.com/winjs/winjs/issues/690#issuecomment-61637832 (includes code snippet)

MonoTouch Dialog elements are not updating/repainting themselves

I have the following in a Section:
_favElement = new StyledStringElement (string.Empty);
_favElement.Alignment = UITextAlignment.Center;
if (_room.IsFavourite) {
_favElement.Image = UIImage.FromBundle ("Images/thumbs_up.png");
_favElement.Caption = "Unmark as Favourite";
} else {
_favElement.Image = null;
_favElement.Caption = "Mark as Favourite";
}
_favElement.Tapped += favElement_Tapped;
Then when I press the element I want the following to happen:
private void favElement_Tapped ()
{
if (_room.IsFavourite) {
_favElement.Image = null;
_favElement.Caption = "Mark as Favourite";
} else {
_favElement.Image = UIImage.FromBundle ("Images/thumbs_up.png");
_favElement.Caption = "Unmark as Favourite";
}
_room.IsFavourite = !_room.IsFavourite;
}
However the image and text does not change in the actual element when the element is tapped. Is there a refresh method or something that must be called? I've also tried changing the Accessory on Tapped as well and nothing changes. The properties behind do reflect the correct values though.
An alternative to reloading the UITableView is to reload the Element using code like this (copied from Touch.Unit):
if (GetContainerTableView () != null) {
var root = GetImmediateRootElement ();
root.Reload (this, UITableViewRowAnimation.Fade);
}
assuming that your code is in DialogViewController,add this
this.ReloadData();
but in your case I recommend you to use BooleanImageElement

Activating views in regions in Prism

I have problem that I don't seem to be able to solve. I have a created a test project, using MEF and Prism4. I've created a test project where I have 2 views and each of them register themselves inside a region, and also a button in another region. When the button is clicked, I want the view of change to the correct view. The code I think is wrong is below, anyone have any ideas what I am doing wrong here ?
public void Initialize()
{
regionManager.RegisterViewWithRegion(RegionNames.MainRegion, typeof(Views.Module1View));
Button button = new Button() { Content = "Module1" };
button.Click += (o, i) =>
{
var region = this.regionManager.Regions[RegionNames.MainRegion];
if (region != null)
{
region.Activate(typeof(Views.Module1View));
}
};
regionManager.AddToRegion(RegionNames.NavigationRegion, button);
}
I get the following error ...
The region does not contain the specified view.
Parameter name: view
Solved it - amazing what a good nights sleep will do! I had to get the view from the ServiceLocator.
public void Initialize()
{
regionManager.RegisterViewWithRegion(RegionNames.MainRegion, () =>
ServiceLocator.Current.GetInstance<Views.Module2View>());
Button button = new Button() { Content = "Module2" };
button.Click += (o, i) =>
{
var view = ServiceLocator.Current.GetInstance<Views.Module2View>();
var region = this.regionManager.Regions[RegionNames.MainRegion];
if (region != null)
{
region.Activate(view);
}
};
regionManager.AddToRegion(RegionNames.NavigationRegion, button);
}

Resources