Problem with finding instantinated GameObject - object

this is my first post here. I'm doing pong game in Unity, and i have problem finding instantinated gameobject ball, which i want to gets it's position to program AI controlling second Paddle. Here how it looks like:
GameController.cs:
public class GameController : MonoBehaviour
{
public GameObject ballPrefab;
public Text score1Text;
public Text score2Text;
public float scoreCoordinates = 3.4f;
private Ball2 currentBall;
private int score1 = 0;
private int score2 = 0;
// Start is called before the first frame update
void Start()
{
SpawnBall();
}
void SpawnBall()
{
GameObject ballInstance = Instantiate(ballPrefab, transform);
currentBall = ballInstance.GetComponent<Ball2>();
currentBall.transform.position = Vector3.zero;
score1Text.text = score1.ToString();
score2Text.text = score2.ToString();
}
// Update is called once per frame
void Update()
{
if (currentBall != null)
{
if (currentBall.transform.position.x > scoreCoordinates)
{
score1++;
Destroy(currentBall.gameObject);
SpawnBall();
}
if (currentBall.transform.position.x < -scoreCoordinates)
{
score2++;
Destroy(currentBall.gameObject);
SpawnBall();
}
}
}
}
AI.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AI : MonoBehaviour
{
private Rigidbody2D AIrig;
private Ball2 ball;
public float speed = 1f;
// Start is called before the first frame update
void Start()
{
ball = GetComponent<Ball2>();
AIrig = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
ball.transform.position = Vector3.zero;
if (ball.transform.position.y > AIrig.transform.position.y)
{
AIrig.velocity = new Vector2(0, speed);
}
else if (ball.transform.position.y > AIrig.transform.position.y)
{
AIrig.velocity = new Vector2(0, -speed);
}
}
}
Ball2.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball2 : MonoBehaviour
{
public float minXspeed = 0.8f;
public float maxXspeed = 1.2f;
public float minYspeed = 0.8f;
public float maxYspeed = 1.2f;
private Rigidbody2D ballRigidbody;
// Start is called before the first frame update
void Start()
{
ballRigidbody = GetComponent<Rigidbody2D>();
ballRigidbody.velocity = new Vector2(Random.Range(minXspeed, maxXspeed) * (Random.value > 0.5f ? -1 : 1), Random.Range(minYspeed, maxYspeed) * (Random.value > 0.5f ? -1 : 1));
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter2D(Collider2D otherCollider)
{
if (otherCollider.tag == "Limit")
{
if (otherCollider.transform.position.y > transform.position.y && ballRigidbody.velocity.y > 0)
{
ballRigidbody.velocity = new Vector2(ballRigidbody.velocity.x, -ballRigidbody.velocity.y);
}
if (otherCollider.transform.position.y < transform.position.y && ballRigidbody.velocity.y < 0)
{
ballRigidbody.velocity = new Vector2(ballRigidbody.velocity.x, -ballRigidbody.velocity.y);
}
}
else if (otherCollider.tag == "Paddle")
{
if (otherCollider.transform.position.x < transform.position.x && ballRigidbody.velocity.x < 0)
{
ballRigidbody.velocity = new Vector2(-ballRigidbody.velocity.x, ballRigidbody.velocity.y);
}
if (otherCollider.transform.position.x > transform.position.x && ballRigidbody.velocity.x > 0)
{
ballRigidbody.velocity = new Vector2(-ballRigidbody.velocity.x, ballRigidbody.velocity.y);
}
}
}
}
I can't get refference to instantinated object (ball). If anyone could help me i will be grateful.

All right, i get it, here is the solution:
I've modified AI.cs, now it looks like:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AI : MonoBehaviour
{
private Rigidbody2D AIrig;
public GameObject ball;
public float speed = 1f;
// Start is called before the first frame update
void Start()
{
AIrig = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
ball = GameObject.Find("Ball(Clone)");
if (ball.transform.position.y > AIrig.transform.position.y)
{
AIrig.velocity = new Vector2(0, speed);
}
else if (ball.transform.position.y < AIrig.transform.position.y)
{
AIrig.velocity = new Vector2(0, -speed);
}
}
}

I am not exactly clear where are those scripts attached.
So, I don't know if your getComponent will work.
GetComponent will work if you have scripts attached to the same object
Otherwise, you have can consider options like FindObjectOfType(), FindGameObjectsWithTag(string tag), GameObject.Find("object name") and more...
But I think the best way to do it, is if you want one object reference then store the instantiated ball to a public variable, and access it through it.
And if you are instantiating multiple objects, then store them in the public list in GameController. Then in the start function take the reference of the game controller and you will have access to those public variables.

Related

I want to check if 4 Conditions are meet something should happen

i am still new to c# i am trying to write a code that would check my if condition if 4 conditions are met then something happens.
Here is my codes:enter code here
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NameTrans : MonoBehaviour {
public string thename;
public GameObject inputField;
public GameObject textDisplay;
public GameObject textDisplay2;
// Use this for initialization
public void Showname () {
thename = inputField.GetComponent<Text>().text;
if (thename.ToUpper().Contains("Man".ToUpper()) || thename.ToUpper().Contains("Dog".ToUpper()))
{
textDisplay2.SetActive(false);
textDisplay.SetActive(true);
textDisplay.GetComponent<Text>().text = "WOrks" ;
}
else
{
textDisplay.SetActive(false);
textDisplay2.SetActive(true);
textDisplay2.GetComponent<Text>().text = "Not WORK" ;
}
}
}
In case only one of 4 conditions needs to be met, you are already doing the right thing. You just need to add the missing two condidions.
if((thename.ToUpper().Contains("Man".ToUpper())) ||
(thename.ToUpper().Contains("Dog".ToUpper())) ||
(thename.ToUpper().Contains("Cat".ToUpper())) ||
(thename.ToUpper().Contains("Fish".ToUpper())))
{
//do something
}
else
{
//do something else
}
If all 4 conditions need to be met, you need to connect the conditions with "&&" instead of "||".
if((thename.ToUpper().Contains("Man".ToUpper())) &&
(thename.ToUpper().Contains("Dog".ToUpper())) &&
(thename.ToUpper().Contains("Cat".ToUpper())) &&
(thename.ToUpper().Contains("Fish".ToUpper())))
{
//do something
}
else
{
//do something else
}
UPDATE with new info
If you want to check if any 4 of x conditions are true, I would probably do something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NameTrans : MonoBehaviour {
public string thename;
public GameObject inputField;
public GameObject textDisplay;
public GameObject textDisplay2;
private string[] conditions;
private int counter;
// Use this for initialization
public void Showname () {
thename = inputField.GetComponent<Text>().text;
conditions = {"Man", "Dog", "Cat", "Fish", "Goat",
"Frog", "Bird", "Alligator"};
counter = 0;
foreach(string cond in conditions)
{
if (thename.ToUpper().Contains(cond.ToUpper())
{
counter += 1;
if (counter >= 4)
{
break;
}
}
}
if (counter >= 4)
{
textDisplay2.SetActive(false);
textDisplay.SetActive(true);
textDisplay.GetComponent<Text>().text = "WOrks" ;
}
else
{
textDisplay.SetActive(false);
textDisplay2.SetActive(true);
textDisplay2.GetComponent<Text>().text = "Not WORK" ;
}
}
}
Thanks alot `enter code here Paddex for the answer.
For those using unity Add New string[]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NewBehaviourScript : MonoBehaviour {
public string thename;
public GameObject inputField;
public GameObject textDisplay;
public GameObject textDisplay2;
private string[] conditions;
private int counter;
// Use this for initialization
public void Showname()
{
thename = inputField.GetComponent<Text>().text;
conditions = new string[] {
"Man", "Dog", "Cat", "Fish", "Goat",
"Frog", "Bird", "Alligator"};
counter = 0;
foreach (string cond in conditions)
{
if (thename.ToUpper().Contains(cond.ToUpper()))
{
counter += 1;
if (counter >= 4)
{
break;
}
}
}
if (counter >= 4)
{
textDisplay2.SetActive(false);
textDisplay.SetActive(true);
textDisplay.GetComponent<Text>().text = "WOrks";
}
else
{
textDisplay.SetActive(false);
textDisplay2.SetActive(true);
textDisplay2.GetComponent<Text>().text = "Not WORK";
}
}
}

How do I implement iOS "shake to undo" in Xamarin Forms, only on apple devices?

This blog describes how to perform shake to undo, however I'd like to create some kind of base class (in Xamarin forms) that I can reuse this code:
#region respond to shaking (OS3+)
public override bool CanBecomeFirstResponder {
get {
return true;
}
}
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
this.BecomeFirstResponder();
}
public override void ViewWillDisappear (bool animated)
{
this.ResignFirstResponder();
base.ViewWillDisappear (animated);
}
public override void MotionEnded (UIEventSubtype motion, UIEvent evt)
{
Console.WriteLine("Motion detected");
if (motion == UIEventSubtype.MotionShake)
{
Console.WriteLine("and was a shake");
// Do your application-specific shake response here...
Update();
}
}
#endregion
I'm not sure where to begin... to create a base class that only applies to iOS.
Is there any kind of Dependency injection I can use with XamarinForms for this runtime implementation of a base class for all views?
Explaination
The good news is we can take advantage of the Device Motion NuGet Package in lieu of Custom Renderers!
We can implement the shake functionality in a base NavgiationPage class and use this as our MainPage.
I've also included logic in the App constructor that only implements the ShakeListenerNavigationPage for iOS.
Code
using System;
using System.Diagnostics;
using Xamarin.Forms;
using DeviceMotion.Plugin;
using DeviceMotion.Plugin.Abstractions;
namespace YourNamespace
{
public class App : Application
{
public App()
{
NavigationPage navigationPage;
switch(Device.RuntimePlatform)
{
case Device.iOS:
navigationPage = new ShakeListenerNavigationPage(new MyPage());
break;
default:
navigationPage = new NavigationPage(new MyPage());
break;
}
MainPage = navigationPage;
}
}
public class ShakeListenerNavigationPage : NavigationPage
{
#region Constant Fields
const int _shakeDetectionTimeLapse = 250;
readonly double _shakeThreshold;
#endregion
#region Fields
bool _hasUpdated;
DateTime _lastUpdate;
double _lastX, _lastY, _lastZ;
#endregion
#region Constructors
public ShakeListenerNavigationPage(Page root) : base(root)
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
_shakeThreshold = 20;
break;
default:
_shakeThreshold = 800;
break;
}
CrossDeviceMotion.Current.Start(MotionSensorType.Accelerometer, MotionSensorDelay.Default);
CrossDeviceMotion.Current.SensorValueChanged += HandleSensorValueChanged;
}
#endregion
#region Methods
void HandleSensorValueChanged(object sender, SensorValueChangedEventArgs e)
{
if (e.SensorType == MotionSensorType.Accelerometer)
{
double x = ((MotionVector)e.Value).X;
double y = ((MotionVector)e.Value).Y;
double z = ((MotionVector)e.Value).Z;
var currentTime = DateTime.Now;
if (_hasUpdated == false)
{
_hasUpdated = true;
_lastUpdate = currentTime;
}
else
{
var hasMinimumTimeElapsed = (currentTime - _lastUpdate).TotalMilliseconds > _shakeDetectionTimeLapse;
if (!hasMinimumTimeElapsed)
return;
_lastUpdate = currentTime;
var timeSinceLastShakeInMilliseconds = (currentTime - _lastUpdate).TotalMilliseconds;
var totalMovementDistance = x + y + z - _lastX - _lastY - _lastZ;
var shakeSpeed = Math.Abs(totalMovementDistance) / timeSinceLastShakeInMilliseconds * 10000;
Debug.WriteLine($"Shake Speed: {shakeSpeed}");
if (shakeSpeed > _shakeThreshold)
HandleShake();
}
_lastX = x;
_lastY = y;
_lastZ = z;
}
}
void HandleShake()
{
Device.BeginInvokeOnMainThread(async () => await DisplayAlert("Shake Detected", "You shook your device!", "Ok"));
}
#endregion
}
}
Sample App
Here's a sample Xamarin.Forms app where I implemented this feature!
https://github.com/brminnick/InvestmentDataSampleApp

Resources won't load in my scene at run-time in unity 5?

I have implemented a helper script using a youtube example. But when I use the Resource method in my other scripts the compiler gives an null Reference error. What am I doing wrong.
this is my helper script and how the resources are called.
using UnityEngine;
using System.Collections;
namespace Helper
{
public class Resource
{
public static string AnimatorController = "System/PlayerAnimator";
}
}
The path according to my scene is
Assets/Resources/System/PlayerAnimator.controller
This is how I used in my other scripts to load the resources
private Animator _animator;
private RuntimeAnimatorController _animatorController;
_animatorController = Resources.Load(Resource.AnimatorController) as RuntimeAnimatorController;
_animator.runtimeAnimatorController = _animatorController;
What is the fix for this?
I added both of the scripts. Non didn't work
PlayerCharctor.cs
using UnityEngine; using System.Collections; using Helper;
[RequireComponent(typeof(NetworkView))] [RequireComponent(typeof(CharacterController))] [RequireComponent(typeof(Animator))]
[RequireComponent(typeof(PlayerMotor))] [RequireComponent(typeof(PlayerCamera))]
[AddComponentMenu("Scripts/Player/PlayerCharacter")] public class PlayerCharacter : MonoBehaviour {
#region Variables & Properties (Private)
private CharacterController _controller;
private Animator _animator;
private RuntimeAnimatorController _animatorController;
#endregion
#region Variables & Properties (Public)
public CharacterController Controller
{
get
{
return _controller;
}
}
public Animator Animator
{
get
{
return _animator;
}
}
#endregion
#region Unity Event Funtions
void awake()
{
_animator = GetComponent<Animator>();
_controller = GetComponent<CharacterController>();
}
// Use this for initialization
void Start ()
{
//Ensure networkview Component exists
if (GetComponent<NetworkView>() != null)
{
//Ensure that initialization only executes if this is a valid instance
if (GetComponent<NetworkView>().isMine || Network.peerType == NetworkPeerType.Disconnected)
{
//Load in the AnimatorController at runtime
_animatorController = Resources.Load(Resource.AnimatorController) as RuntimeAnimatorController;
_animator.runtimeAnimatorController = _animatorController;
_controller.center = new Vector3(0f, 1f, 0f);
_controller.height = 1.8f;
}
else
{
enabled = false;
}
}
else
{
Debug.Log("Attach a NetWorkViewComponent!!");
}
}
// Update is called once per frame
void Update ()
{
}
#endregion
#region Methods
#endregion Methods }
AND the Helper script
using UnityEngine;
using System.Collections;
namespace Helper
{
#region Referance Cache
public class PlayerInput
{
public static string Horizontal = "Horizontal";
public static string Vertical = "vertical";
public static string Jump = "Jump";
public static string RightX = "Mouse X";
public static string RightY = "Mouse Y";
}
public class GameTag
{
//System tags
public static string Untagged = "Untagged";
public static string Respawn = "Respawn";
public static string Finish = "Finish";
public static string EditorOnly = "EditorOnly";
public static string MainCamera = "MainCamera";
public static string Player = "Player";
public static string GameController = "GameController";
public static string PlayerCamera = "PlayerCamera";
}
public class Resource
{
public static string AnimatorController= "System/PLController"; //Changed the name of the controller (new one)
}
public static class AnimatorConditions
{
public static string Speed = "Speed";
public static string Direction = "Direction";
public static string Grounded = "Grounded";
public static string AirVelocity = "AirVelocity";
}
#endregion
#region FSM Enumerations (Finite State Machine)
public enum CameraState
{
Normal,
Target
}
public enum SpeedState
{
Walk,
Run,
Sprint
}
#endregion
#region Object Structures
public struct CameraTargetObject
{
private Vector3 position;
private Transform xForm;
public Vector3 Position
{
get
{
return position;
}
set
{
position = value;
}
}
public Transform XForm
{
get
{
return xForm;
}
set
{
xForm = value;
}
}
public void Init(string camName, Vector3 pos, Transform transform, Transform parent)
{
position = pos;
xForm = transform;
xForm.name = camName;
xForm.parent = parent;
xForm.localPosition = Vector3.zero;
xForm.localPosition = position;
}
}
public struct CameraMountPoint
{
private Vector3 position;
private Transform xForm;
public Vector3 Position
{
get
{
return position;
}
set
{
position = value;
}
}
public Transform XForm
{
get
{
return xForm;
}
set
{
xForm = value;
}
}
public void Init(string camName, Vector3 pos, Transform transform, Transform parent)
{
position = pos;
xForm = transform;
xForm.name = camName;
xForm.parent = parent;
xForm.localPosition = Vector3.zero;
xForm.localPosition = position;
}
}
#endregion
}
There is no problem in your code of loading assets from Resources. But in fact it may be because of your _animator variable. I am 99% sure that it is null. First get the Animator component like _animator = GetComponent<Animator>(); then assign its RuntimeAnimatorController.

UICollectionView - Curious flow layout

I'm experiencing with the UICollectionView(Controller) for the first time. Actually it should be as simple as working with TableViews, it's not though.
Rather than showing all images in a flow (several rows) just the top row is displayed. All the other images are somewhere... scrolling is enabled but nothing happens, no bouncing, no scrolling, ...
And after an orientation change (and back) some more images are visible but they appear randomly. After every orientation change they appear at an other location.
My example should have 7 images.
My properties in IB:
First time:
After rotating (and back):
And my source code to implement the photo gallery.
using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Collections.Generic;
using Xamarin.Media;
using MonoTouch.AssetsLibrary;
using MonoTouch.CoreGraphics;
using System.Diagnostics;
using System.Linq;
using System.Drawing;
namespace B2.Device.iOS
{
public partial class TagesRapportDetailRegieBilderCollectionViewController : UICollectionViewController
{
private const string Album = "Rapport";
public TagesRapportDetailRegieBilderSource Source { get; private set; }
private TagesRapportDetailRegieBilderDelegate _delegate;
public TagesRapportDetailRegieBilderCollectionViewController (IntPtr handle) : base (handle)
{
Source = new TagesRapportDetailRegieBilderSource(this);
_delegate = new TagesRapportDetailRegieBilderDelegate(this);
// Delegate - Muss im konstruktor sein. ViewDidLoad geht nicht!
CollectionView.Delegate = _delegate;
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Cell Klasse registrieren
CollectionView.RegisterClassForCell (typeof(ImageCell), new NSString("imageCell"));
// DataSource
CollectionView.Source = Source;
// Bilder laden
LoadImages();
}
private void LoadImages()
{
Source.Images.Clear();
var assetsLibrary = new ALAssetsLibrary();
assetsLibrary.Enumerate(ALAssetsGroupType.Album, GroupsEnumeration, GroupsEnumerationFailure);
}
private void GroupsEnumeration(ALAssetsGroup group, ref bool stop)
{
if (group != null && group.Name == Album)
{
//notifies the library to keep retrieving groups
stop = false;
//set here what types of assets we want,
//photos, videos or both
group.SetAssetsFilter(ALAssetsFilter.AllPhotos);
//start the asset enumeration
//with ALAssetsGroup's Enumerate method
group.Enumerate(AssetsEnumeration);
CollectionView.ReloadData();
}
}
private void AssetsEnumeration(ALAsset asset, int index, ref bool stop)
{
if (asset != null)
{
//notifies the group to keep retrieving assets
stop = false;
//use the asset here
var image = new UIImage(asset.AspectRatioThumbnail());
Source.Images.Add(image);
}
}
private void GroupsEnumerationFailure(NSError error)
{
if (error != null)
{
new UIAlertView("Zugriff verweigert", error.LocalizedDescription, null, "Schliessen", null).Show();
}
}
}
public class TagesRapportDetailRegieBilderDelegate : UICollectionViewDelegateFlowLayout
{
private TagesRapportDetailRegieBilderCollectionViewController _controller;
public TagesRapportDetailRegieBilderDelegate(TagesRapportDetailRegieBilderCollectionViewController controller)
{
_controller = controller;
}
public override System.Drawing.SizeF GetSizeForItem(UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
var size = _controller.Source.Images[indexPath.Row].Size.Width > 0
? _controller.Source.Images[indexPath.Row].Size : new SizeF(100, 100);
size.Width /= 3;
size.Height /= 3;
return size;
}
public override UIEdgeInsets GetInsetForSection(UICollectionView collectionView, UICollectionViewLayout layout, int section)
{
return new UIEdgeInsets(50, 20, 50, 20);
}
}
public class TagesRapportDetailRegieBilderSource : UICollectionViewSource
{
private TagesRapportDetailRegieBilderCollectionViewController _controller;
public List<UIImage> Images { get; set; }
public TagesRapportDetailRegieBilderSource(TagesRapportDetailRegieBilderCollectionViewController controller)
{
_controller = controller;
Images = new List<UIImage>();
}
public override int NumberOfSections(UICollectionView collectionView)
{
return 1;
}
public override int GetItemsCount(UICollectionView collectionView, int section)
{
return Images.Count;
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.DequeueReusableCell(new NSString("imageCell"), indexPath) as ImageCell;
cell.Image = Images[indexPath.Row];
return cell;
}
}
public class ImageCell : UICollectionViewCell
{
UIImageView imageView;
[Export ("initWithFrame:")]
public ImageCell (System.Drawing.RectangleF frame) : base (frame)
{
imageView = new UIImageView(frame);
imageView.AutoresizingMask = ~UIViewAutoresizing.None;
ContentView.AddSubview (imageView);
}
public UIImage Image
{
set
{
imageView.Image = value;
}
}
}
}
If all you want to do is displaying the cells in an uniform grid, overriding GetSizeForItem shouldn't be necessary in the first place. Just configure the cellsize property of your flow layout either in IB or programatically during ViewDidLoad and be done with it.
There's another problem with your code:
group.Enumerate(AssetsEnumeration)
This will run asynchronously. That means:
CollectionView.ReloadData();
Will only cover a small subset of your images. It would be better to issue a ReloadData() when group == null, which indicates that the enumeration is complete.
You could also avoid ReloadData alltogether and call CollectionView.InsertItem() everytime you've added an image. This has the benefit of your items become immediately visible instead of all of them at once after everything has been enumerated - which may take some time (on the device). The downside is that you've got to be careful to not run into this.

How to reflect changes in viewmodel to tableviewcell view with binding in MVVMcross

Am a little stuck with getting changes reflected from the ViewModel to the View when used in a MvxBindableTableViewCell. I am using the vNext branch of MvvmCross on iOS.
Everything is set up properly and the initial values are visible when loading/showing the list for the first time. The list is a ObservableCollection<T> and the ViewModels inherit from MvxViewModel (thus implements INotifyPropertyChanged).
The main ViewModel looks like this:
public abstract class BaseViewModel : MvxViewModel, IMvxServiceConsumer
{
//... just regular implementation
}
public class UploadListViewModel: BaseViewModel
{
private readonly IUploadItemTasks uploadItemTasks;
private readonly IPhotoPickerService photoPickerService;
public IObservableCollection<UploadItemViewModel> Uploads { get { return this.LoadUploadItems(); } }
public UploadListViewModel()
{
this.uploadItemTasks = this.GetService<IUploadItemTasks>();
this.photoPickerService = this.GetService<IPhotoPickerService>();
}
private IObservableCollection<UploadItemViewModel> LoadUploadItems()
{
using (var unitOfWork = UnitOfWork.Start ())
{
return new SimpleObservableCollection<UploadItemViewModel>(uploadItemTasks.GetAll());
}
}
public void StartUpload ()
{
if (this.Uploads == null || this.Uploads.Count == 0) {
ReportError("Error", "No images to upload");
return;
}
this.Uploads.ForEach (uploadItem => PostCallback (uploadItem));
}
private void PostCallback (UploadItemViewModel uploadAsset)
{
IProgressReporter progressReporter = uploadAsset;
this.photoPickerService.GetAssetFullImage(uploadAsset.ImageUrl,
(image) => {
UIImage fullImage = image;
NSData jpeg = fullImage.AsJPEG();
byte[] jpegBytes = new byte[jpeg.Length];
System.Runtime.InteropServices.Marshal.Copy(jpeg.Bytes, jpegBytes, 0, Convert.ToInt32(jpeg.Length));
MemoryStream stream = new MemoryStream(jpegBytes);
Uri destinationUrl = new Uri(uploadAsset.DestinationUrl + "&name=" + uploadAsset.Name + "&contentType=image%2FJPEG");
//TO DO: Move this to plugin
var uploader = new Uploader().UploadPicture (destinationUrl, stream, UploadComplete, progressReporter);
uploader.Host = uploadAsset.Host;
ThreadPool.QueueUserWorkItem (delegate {
uploader.Upload ();
jpeg = null;
});
});
}
private void UploadComplete (string name)
{
if (name == null){
ReportError("Error","There was an error uploading the media.");
} else
{
//ReportError("Succes", name);
}
}
The item ViewModel looks like:
public interface IProgressReporter
{
float Progress { get; set;}
}
public abstract class BaseAssetViewModel: BaseViewModel, IBaseAssetViewModel
{
//... just regular properties
}
public class UploadItemViewModel: BaseAssetViewModel, IProgressReporter
{
public UploadItemViewModel(): base()
{
}
private float progress;
public float Progress {
get {
return this.progress;
}
set {
this.progress = value;
this.RaisePropertyChanged(() => Progress);
}
}
}
The View for the items inherits from MvxBindableTableViewCell and has the property:
private float progress;
public float ProgressMarker {
get {
return progress;
}
set {
progress = value;
// change progressbar or textfield here
}
}
The tableviewcell is bounded to the UploadItemViewModel via the BindingText:
public const string BindingText = #"ProgressMarker Progress, Converter=Float;";
The Uploader class mentioned in the snippet of UploadListViewModel implements a private method which tries to set the progress on the IProgressReporter.
float progressValue;
void SetProgress (float newvalue)
{
progressValue = newvalue;
this.dispatcher.InvokeOnMainThread (delegate {
if (ProgressReporter != null)
ProgressReporter.Progress = progressValue;
});
}
During the first viewing of the list I can see that the properties in both the ViewModel and View are being hit but when I update the ViewModel via the interface IProgressReporter with a new value in Progress the View in the tableviewcell is not updated nor the property is being called.
What am I doing wrong or what am I missing here?
UPDATE: Check the answer to this question.
I found why the binding didn't work. I was replacing the ObservableCollection over and over again.. I changed that piece of code as stated below and now it reflects the changes made to the UploadItemViewModel in the View of the cell.
private IObservableCollection<UploadItemViewModel> uploads;
private IObservableCollection<UploadItemViewModel> LoadUploadItems()
{
if (uploads == null)
{
using (var unitOfWork = UnitOfWork.Start ())
{
uploads = new SimpleObservableCollection<UploadItemViewModel>(uploadItemTasks.FindAll());
}
}
return uploads;
}

Resources