Following the tutorial, I'm developping my fist app. In this app, I want to implements swipe for navigation.
But I'm facing an issue : I cannot access to my component object.
I'm using NativeScript + Angular.
constructor(
private _router: Router
, private _routeParams: RouteParams
, private _signalListService: SignalListService
, private _page : Page
) {
console.log('ViewPageConstructor : ' + this);
this.itemId = this._routeParams.get("id");
this._page.on("swipe", function(args: gestures.SwipeGestureEventData) {
console.log("Swipe Direction From event function: " + args.direction);
console.log(args); // [object Object]
console.log(_page); // Page(148)
console.log(this); // undefined
this.swipe(args); // Nothing happen ... no error ...
});
}
swipe(args: gestures.SwipeGestureEventData) {
console.log("Swipe Direction From event function: " + args.direction);
this._router(...
}
I never get to the swipe function. There is non error in the command log ...
Thanks
The closure.. this inside your swipe event is not this from your constructor.
What you can do us to "cache" your constructor reference:
export class AppComponent {
constructor(private page: Page) {
var that = this;
this.page.on("swipe", function(args: gestures.SwipeGestureEventData) {
console.log("Swipe Direction From event function: " + args.direction);
that.onSwipe();
})
}
public onSwipe() {
console.log("onSwipe triggered");
}
}
Related
When a user authenticates correctly, it will be directed to the HomeViewModel. I want to remove the possibility that it can return to the login screen so I have created a Custom Presenter to remove all the screens that are below the new screen.
The implementation is as follows:
public class CustomPresenter: MvxFormsIosPagePresenter
{
public CustomPresenter(UIWindow window, MvxFormsApplication mvxFormsApp)
: base(window, mvxFormsApp)
{
}
public override void Show(MvxViewModelRequest request)
{
if (request.PresentationValues?["NavigationCommand"] == "StackClear")
{
var navigation = FormsApplication.MainPage.Navigation;
Debug.WriteLine("Navigation Back Stack Count -> " + navigation.NavigationStack.Count());
navigation.PopToRootAsync();
Debug.WriteLine("Navigation Back Stack Count After PopToRootAsync -> " + navigation.NavigationStack.Count());
return;
}
base.Show(request);
}
}
When the authentication process finishes correctly, I navigate to the home screen by passing a bundle with this special command:
LoginWithFacebookCommand.Subscribe(token => {
Debug.WriteLine("JWT Token -> " + token);
_userDialogs.ShowSuccess(AppResources.Login_Success);
var mvxBundle = new MvxBundle(new Dictionary<string, string> { { "NavigationCommand", "StackClear" } });
ShowViewModel<HomeViewModel>(presentationBundle: mvxBundle);
});
The problem is that it does not change the screen, it stays in the current one. What would be the way to do it correctly ?.
I am using MvvmCross 5.1.1 and MvvmCross.Forms 5.1.1
Thank you very much in advance.
As I understand it, PopToRootAsync() pops everything off the stack to the root. Which means you should then push your view that you wish to navigate to, onto your stack after that method is called i.e. use PushViewController(yourViewController) afterwards. Also, you should be using the new IMvxNavigationService by MvvmCross. You can give this a try:
var navigationService = Mvx.Resolve<IMvxNavigationService>();
LoginWithFacebookCommand.Subscribe(async (token) => {
Debug.WriteLine("JWT Token -> " + token);
_userDialogs.ShowSuccess(AppResources.Login_Success);
await navigationService.Navigate<HomeViewModel>();
});
To clear the backstack you basically need to override the Show method in the presenter and check whether your viewmodel is being called. If it is then set a new array of viewControllers. (Credit to #pnavk!!)
public class CustomPresenter : MvxIosViewPresenter
{
public override void Show(IMvxIosView view, MvxViewModelRequest request)
{
if (MasterNavigationController != null && view.ViewModel.GetType() == typeof(HomeViewModel))
{
var viewController = view as UIViewController;
MasterNavigationController.SetViewControllers(new UIViewController[] { viewController }, true);
}
else
base.Show(view, request);
}
}
Try this:
navigation.SetViewControllers(new UIViewController[] { vc }, true);
vc is the ViewController you want to set as the root of the navigation stack. You will need to get a reference to it which you can using the ViewControllers property on the NavigationController.
true - means you want to animate.
How to detect UI object on Canvas on Touch in android?
For example, I have a canvas that have 5 objects such as Image, RawImage, Buttons, InputField and so on.
When I touch on Button UI object Then do something. Each button do different process when clicked depending.
The code will look like:
private void Update()
{
if (Input.touches.Length <= 0) return;
for (int i = 0; i < Input.touchCount; i++)
{
if (Button1.touch)
if (Input.GetTouch(i).phase == TouchPhase.Began)
login();
else if (Button2.touch && Input.GetTouch(i).phase == TouchPhase.Began)
LogOut();
}
}
So how to do it?
Second:
How to detect Gameobject get touch? Is it same with that above or not?
You don't use the Input API for the new UI. You subscribe to UI events or implement interface depending on the event.
These are the proper ways to detect events on the new UI components:
1.Image, RawImage and Text Components:
Implement the needed interface and override its function. The example below implements the most used events.
using UnityEngine.EventSystems;
public class ClickDetector : MonoBehaviour, IPointerDownHandler, IPointerClickHandler,
IPointerUpHandler, IPointerExitHandler, IPointerEnterHandler,
IBeginDragHandler, IDragHandler, IEndDragHandler
{
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("Drag Begin");
}
public void OnDrag(PointerEventData eventData)
{
Debug.Log("Dragging");
}
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("Drag Ended");
}
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("Clicked: " + eventData.pointerCurrentRaycast.gameObject.name);
}
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Mouse Down: " + eventData.pointerCurrentRaycast.gameObject.name);
}
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log("Mouse Enter");
}
public void OnPointerExit(PointerEventData eventData)
{
Debug.Log("Mouse Exit");
}
public void OnPointerUp(PointerEventData eventData)
{
Debug.Log("Mouse Up");
}
}
2.Button Component:
You use events to register to Button clicks:
public class ButtonClickDetector : MonoBehaviour
{
public Button button1;
public Button button2;
public Button button3;
void OnEnable()
{
//Register Button Events
button1.onClick.AddListener(() => buttonCallBack(button1));
button2.onClick.AddListener(() => buttonCallBack(button2));
button3.onClick.AddListener(() => buttonCallBack(button3));
}
private void buttonCallBack(Button buttonPressed)
{
if (buttonPressed == button1)
{
//Your code for button 1
Debug.Log("Clicked: " + button1.name);
}
if (buttonPressed == button2)
{
//Your code for button 2
Debug.Log("Clicked: " + button2.name);
}
if (buttonPressed == button3)
{
//Your code for button 3
Debug.Log("Clicked: " + button3.name);
}
}
void OnDisable()
{
//Un-Register Button Events
button1.onClick.RemoveAllListeners();
button2.onClick.RemoveAllListeners();
button3.onClick.RemoveAllListeners();
}
}
If you are detecting something other than Button Click on the Button then use method 1. For example, Button down and not Button Click, use IPointerDownHandler and its OnPointerDown function from method 1.
3.InputField Component:
You use events to register to register for InputField submit:
public InputField inputField;
void OnEnable()
{
//Register InputField Events
inputField.onEndEdit.AddListener(delegate { inputEndEdit(); });
inputField.onValueChanged.AddListener(delegate { inputValueChanged(); });
}
//Called when Input is submitted
private void inputEndEdit()
{
Debug.Log("Input Submitted");
}
//Called when Input changes
private void inputValueChanged()
{
Debug.Log("Input Changed");
}
void OnDisable()
{
//Un-Register InputField Events
inputField.onEndEdit.RemoveAllListeners();
inputField.onValueChanged.RemoveAllListeners();
}
4.Slider Component:
To detect when slider value changes during drag:
public Slider slider;
void OnEnable()
{
//Subscribe to the Slider Click event
slider.onValueChanged.AddListener(delegate { sliderCallBack(slider.value); });
}
//Will be called when Slider changes
void sliderCallBack(float value)
{
Debug.Log("Slider Changed: " + value);
}
void OnDisable()
{
//Un-Subscribe To Slider Event
slider.onValueChanged.RemoveListener(delegate { sliderCallBack(slider.value); });
}
For other events, use Method 1.
5.Dropdown Component
public Dropdown dropdown;
void OnEnable()
{
//Register to onValueChanged Events
//Callback with parameter
dropdown.onValueChanged.AddListener(delegate { callBack(); });
//Callback without parameter
dropdown.onValueChanged.AddListener(callBackWithParameter);
}
void OnDisable()
{
//Un-Register from onValueChanged Events
dropdown.onValueChanged.RemoveAllListeners();
}
void callBack()
{
}
void callBackWithParameter(int value)
{
}
NON-UI OBJECTS:
6.For 3D Object (Mesh Renderer/any 3D Collider)
Add PhysicsRaycaster to the Camera then use any of the events from Method 1.
The code below will automatically add PhysicsRaycaster to the main Camera.
public class MeshDetector : MonoBehaviour, IPointerDownHandler
{
void Start()
{
addPhysicsRaycaster();
}
void addPhysicsRaycaster()
{
PhysicsRaycaster physicsRaycaster = GameObject.FindObjectOfType<PhysicsRaycaster>();
if (physicsRaycaster == null)
{
Camera.main.gameObject.AddComponent<PhysicsRaycaster>();
}
}
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Clicked: " + eventData.pointerCurrentRaycast.gameObject.name);
}
//Implement Other Events from Method 1
}
7.For 2D Object (Sprite Renderer/any 2D Collider)
Add Physics2DRaycaster to the Camera then use any of the events from Method 1.
The code below will automatically add Physics2DRaycaster to the main Camera.
public class SpriteDetector : MonoBehaviour, IPointerDownHandler
{
void Start()
{
addPhysics2DRaycaster();
}
void addPhysics2DRaycaster()
{
Physics2DRaycaster physicsRaycaster = GameObject.FindObjectOfType<Physics2DRaycaster>();
if (physicsRaycaster == null)
{
Camera.main.gameObject.AddComponent<Physics2DRaycaster>();
}
}
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log("Clicked: " + eventData.pointerCurrentRaycast.gameObject.name);
}
//Implement Other Events from Method 1
}
Troubleshooting the EventSystem:
No clicks detected on UI, 2D Objects (Sprite Renderer/any 2D Collider) and 3D Objects (Mesh Renderer/any 3D Collider):
A.Check that you have EventSystem. Without EventSystem it can't detect clicks at-all. If you don't have have it, create it yourself.
Go to GameObject ---> UI ---> Event System. This will create an EventSystem if it doesn't exist yet. If it already exist, Unity will just ignore it.
B.The UI component or GameObject with the UI component must be under a Canvas. It means that a Canvas must be the parent of the UI component. Without this, EventSystem will not function and clicks will not be detected.
This only applies to UI Objects. It doesn't apply to 2D (Sprite Renderer/any 2D Collider) or 3D Objects (Mesh Renderer/any 3D Collider).
C.If this is a 3D Object, PhysicsRaycaster is not attached to the camera. Make sure that PhysicsRaycaster is attached to the camera. See #6 above for more information.
D.If this is a 2D Object, Physics2DRaycaster is not attached to the camera. Make sure that Physics2DRaycaster is attached to the camera. See #7 above for more information.
E.If this is a UI object you want to detect clicks on with the interface functions such as OnBeginDrag, OnPointerClick, OnPointerEnter and other functions mentioned in #1 then the script with the detection code must be attached to that UI Object you want to detect click on.
F.Also, if this is a UI Object you want to detect clicks on, make sure that no other UI Object is in front of it. If there is another UI in front of the one you want to detect click on, it will be blocking that click.
To verify that this is not the issue, disable every object under the Canvas except the one you want to detect click on then see if clicking it works.
You can add an EventTrigger Componenet to Your UI elements that already have these Events you just have to pass method/Function on specific event.
You could use OnMouseDown as well. OnMouseDown is called when the user has pressed the mouse button while over the GUIElement or Collider. This event is sent to all scripts of the Collider or GUIElement.
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement; // The new load level needs this
public class ExampleClass : MonoBehaviour
{
void OnMouseDown()
{
// Edit:
// Application.LoadLevel("SomeLevel");
// Application.LoadLevel() is depreciating but still works
SceneManager.LoadScene("SomeLevel"); // The new way to load levels
}
}
Do not use OnMouseDown() for mobile performance and multi-touch issues.
This code works on UI Objects for multi-touches
In my answer, I use Image element with a "Button" tag and it has a ButtonController script with a ButtonDown() public method that should be called when user touches the Image element.
Note: Image element has a 2D Collider.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class TouchScript : MonoBehaviour
{
void Update()
{
PointerEventData pointer = new PointerEventData(EventSystem.current);
List<RaycastResult> raycastResult = new List<RaycastResult>();
foreach (Touch touch in Input.touches)
{
if(touch.phase.Equals(TouchPhase.Began))
{
pointer.position = touch.position;
EventSystem.current.RaycastAll(pointer, raycastResult);
foreach(RaycastResult result in raycastResult)
{
if(result.gameObject.tag == "Button")
{
result.gameObject.GetComponent<ButtonController>().ButtonDown();
}
}
raycastResult.Clear();
}
}
}
}
I want to integrate a custom NS core / third party component into an Angular template. The attributes of my component should also be set in the template.
Given the generated sample from tns, i have done the following steps:
Register my Component:
registerElement("TestView", () => require("./test-view").TestView);
Create Component:
import observable = require("data/observable");
import stackLayout = require("ui/layouts/stack-layout");
import label = require("ui/label");
import button = require("ui/button");
import { View } from "ui/core/view";
import { Property, PropertyChangeData, PropertyMetadata, PropertyMetadataSettings } from "ui/core/dependency-observable";
export class TestView extends stackLayout.StackLayout {
public static userNameProperty = new Property(
"userName",
"TestView",
new PropertyMetadata("", PropertyMetadataSettings.None)
);
public get userName(): string {
return this._getValue(TestView.userNameProperty);
}
public set userName(newName: string) {
this._setValue(TestView.userNameProperty, newName);
}
constructor() {
super();
var counter: number = 0;
var lbl = new label.Label();
var btn = new button.Button();
btn.text = "Tap me " + this.userName + "!";
btn.on(button.Button.tapEvent, (args: observable.EventData) => {
lbl.text = "Tap " + counter++;
});
this.addChild(lbl);
this.addChild(btn);
}
}
Use it in Angular template:
<StackLayout class="p-20">
<TestView userName="Felix"></TestView>
</StackLayout>
The component displays, but the button text does not show "Tap me [userName]", because userName is undefined.
What is the correct way to pass arguments as attributes for the component?
Update
Having read Data Binding and Properties, i augmented above code sample with definition of a userName property. But its still not set, quite frustrating...
Can anybody give some insight? Many thanks.
Finally got it working.
There has to be established Data Binding between
Angular template and custom Component TestView by encapsulating the property userName in a Property type of the ui/core/dependency-observable module (see above code).
the component and it's button by binding the component's userName source property to the button target text property (you can also set this by a default BindingContext).
var btn = new button.Button();
var buttonBindingOptions = {
expression: "'Tap me ' + userName + '!'",
sourceProperty: "userName",
targetProperty: "text",
twoWay: false
};
btn.bind(buttonBindingOptions, this);
In the parent Appcomponent user can by dynamically set like this:
app.component.ts:
export class AppComponent {
user : string;
constructor() {
this.user = "Felix";
setTimeout(()=> {this.user = "Other"}, 1000);
}
}
app.html:
<TestView [userName]="user"></TestView>
I would like to capture all events within a GWT frame. I've found several ways to do this, but they only return mousemove and mouseout events. I also need keypresses, input, etc. The goal is to capture the events and send them to another client by using websockets, and then replicate them on the other side (co-browsing).
I am using a page on the same domain within the frame.
public class ESinkFrame extends Frame implements EventListener {
public ESinkFrame(String src){
super(src);
DOM.sinkEvents(getElement(), Event.KEYEVENTS);
DOM.sinkEvents(getElement(), Event.MOUSEEVENTS);
}
public void onBrowserEvent(Event event) {
System.out.println( "sunk event: " + DOM.eventGetTypeString(event) );
}
}
And when I use it, I also try to attach a different way of grabbing the events.
ESinkFrame frame = new ESinkFrame("http://127.0.0.1:8888/other.html");
RootPanel.get().add(frame);
FrameElement frameElt = frame.getElement().cast();
Document frameDoc = frameElt.getContentDocument();
BodyElement body = frameDoc.getBody();
Element el = body.cast();
DOM.setEventListener(el, new EventListener()
{
public void onBrowserEvent(Event event)
{
Window.alert("test");
}
});
DOM.sinkEvents(el, Event.KEYEVENTS);
Event.addNativePreviewHandler(new NativePreviewHandler(){
public void onPreviewNativeEvent(NativePreviewEvent event) {
String eventName = event.getNativeEvent().getType();
if (event.isFirstHandler() /* && (event.getTypeInt() & Event.MOUSEEVENTS) == 0*/)
System.out.println("PreviewHandler: " + eventName);
}
});
If I have a button on the timeline for the first few frames, but then I remove it...
Do I have to worry about removing the event listener for the button (MovieClip) if the button has be removed from the stage?
I'm writing a Document Class even though I'm using objects on the timeline.
You could use the removedFromStage event to clear up any event listeners on your button instance:
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
public class Button extends MovieClip
{
public function Button():void
{
addListeners();
}
private function addListeners():void
{
this.addEventListener(Event.ADDED_TO_STAGE, addedHandler);
this.addEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
this.addEventListener(MouseEvent.CLICK, clickHandler);
}
private function addedHandler(event:Event):void
{
trace("button added");
}
private function removedHandler(event:Event):void
{
trace("button removed");
removeListeners();
}
private function clickHandler(event:MouseEvent):void
{
trace("button clicked");
}
private function removeListeners():void
{
this.removeEventListener(Event.ADDED_TO_STAGE, addedHandler);
this.removeEventListener(Event.REMOVED_FROM_STAGE, removedHandler);
this.removeEventListener(MouseEvent.CLICK, clickHandler);
trace("has added listener: " + this.hasEventListener(Event.ADDED_TO_STAGE));
trace("has removed listener: " + this.hasEventListener(Event.REMOVED_FROM_STAGE));
trace("has click listener: " + this.hasEventListener(MouseEvent.CLICK));
}
}
}