UISlider not updating to bound ViewModel Property - ios

I've got a UISeekbar and a UILabel located inside an MvxView
_currentPositionText = new UILabel { TextColor = UIColor.White };
_seekBar = new UISlider
{
MinValue = 0,
Continuous = true,
AutoresizingMask = UIViewAutoresizing.FlexibleWidth
};
_seekBar.TouchDown += OnTouchDown;
_seekBar.TouchUpInside += OnTouchUpInside;
_seekBar.TouchDragInside += OnDragInside;
I'm setting the binds as follows
set.Bind(_seekBar).For(sb => sb.Value).To(vm => _viewModel.CurrentPositionMsec);
set.Bind(_seekBar).For(sb => sb.MaxValue).To(vm => _viewModel.DurationMsec);
set.Bind(_currentPositionText).To(vm => vm.CurrentPositionText);
If I add some trace code inside the get of the CurrentPositionMsec, I can see the property updating once per second (as expected).
note: That same method also updates the CurrentPositionText by simply formatting the milliseconds into a TimeSpan.
The binding for the _currentPositionText is updating as expected, but the _seekBar doesn't update the slider position.
The expected result is for the slider to update once per second based on the CurrentPositionMsec.
I have this working in Android with the following binding, and both Android and iOS are sharing the same viewModel.
<SeekBar
android:Foo=""
android:Bar=""
local:MvxBind="Max DurationMsec; Progress CurrentPositionMsec" />
Am I not setting something up right? This "should" work AFAIK.

Reordering the bindings does not appear to yield any better results
// binding MaxValue first doesn't fix (lambda)
set.Bind(_seekBar).For(sb => sb.MaxValue).To(vm => _viewModel.DurationMsec);
set.Bind(_seekBar).For(sb => sb.Value).To(vm => _viewModel.CurrentPositionMsec);
Changing the binding to use a string rather than a lambda DID prove to solve the issue.
// using a string did enable the bindings to work as expected.
set.Bind(_seekBar).For("MaxValue").To(vm => _viewModel.DurationMsec);
set.Bind(_seekBar).For("Value").To(vm => _viewModel.CurrentPositionMsec);
We have now chosen to use strings as a standard in our code base. It does leave room for mistakes since we don't get compile time errors, but at least it ensures the bindings work.

I added a second SeekBar to the SeekView sample in https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/ApiExamples
This code seemed to work well:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var label = new UILabel(new RectangleF(10, 100, 100, 30));
label.Text = "Slide me:";
Add(label);
var seek = new UISlider(new RectangleF(110, 100, 200, 30));
seek.MinValue = 0;
seek.MaxValue = 100;
Add(seek);
var seek2 = new UISlider(new RectangleF(110, 160, 200, 30));
seek2.MinValue = 0;
seek2.MaxValue = 100;
Add(seek2);
var mirrorLabel = new UILabel(new RectangleF(110, 130, 200, 30));
mirrorLabel.TextColor = UIColor.Blue;
Add(mirrorLabel);
var set = this.CreateBindingSet<SeekView, SeekViewModel>();
set.Bind(seek).To(vm => vm.SeekProperty);
set.Bind(seek2).To(vm => vm.SeekProperty);
set.Bind(mirrorLabel).To(vm => vm.SeekProperty);
set.Apply();
}
Obviously this only exercises the current value and not the maximum - but hopefully getting this working will help.
My only suggestion might be to try reordering your bindings - so that MaxValue is set before Value is. Or perhaps try using a fixed MaxValue and then using a value converter (or other mechanism) to scale the current value.

Related

PixiJS: Constantly updating PIXI.RenderTexture dumps browser memory

There are two sprites, one is dynamic and second have to be constantly updated with first one through PIXI.RenderTexture()
let sprite = new PIXI.Sprite(), tmp = new PIXI.Sprite();
//some manipulations with these sprites
app.ticker.add((delta_) => {
...
updateTexture(w, h); //width is dynamic value
...
})
Here is updateTexture() function
function updateTexture(w_, h_){
let renderTexture = PIXI.RenderTexture.create({ width: w_, height: h_});
tmp.texture = sprite.texture;
app.renderer.render(tmp, renderTexture);
app.renderer.clear();
placeholder_.texture = renderTexture;
placeholder_.texture.update();
}
And it seems that let renderTexture = PIXI.RenderTexture.create({ width: w_, height: h_}); creates a new instance in memory dumping it every time up to browser crash.
app.renderer.clear() doesn't helps.
Any ideas for PixiJS version 6.5.5?

How to create UIStackView from code correctly

I can't understand why this layout won't render the way i need it. Completing the requirement on android with linearlayouts should be equivalent to this?
What i get:
What i need:
My code:
// my stackview only renders at all if i pass any height > 0 && width > 0,
// but i would love this to be as small as possible, given that all children
// are rendered.
var stackView = new UIStackView(View.Frame);
stackView.Axis = UILayoutConstraintAxis.Vertical;
stackView.Distribution = UIStackViewDistribution.Fill;
foreach (var deviceVm in devices)
{
// i would prefer this not to have a rect parameter and scale as needed.
var descriptionLabel = new UILabel(new CGRect(0, 0, View.Bounds.Width, 20));
descriptionLabel.Text = deviceVm.Description;
descriptionLabel.TextColor = UIColor.Blue;
descriptionLabel.ContentMode = UIViewContentMode.ScaleToFill;
stackView.AddArrangedSubview(descriptionLabel);
}
// i assume this should make it as small as possible, as big as necessary?
stackView.SizeToFit();
var scrollView = new UIScrollView(View.Frame);
scrollView.BackgroundColor = UIColor.Yellow;
scrollView.AddSubview(stackView);
View.AddSubview(scrollView);
Can someone spot a mistake i am doing here? Am i configuring the StackView incorrectly or is this control not the equivalent of LinearLayout(android) / StackPanel(wpf) ?
PS: Sorry for eye cancer paint pictures.

How do I fix a nil value error produced when calling in a widget method in Corona SDK?

How do I fix a nil value error produced when calling in a widget method in Corona SDK?
local widget = require('widget')
-- Text Box
local myTextField = widget.newTextField(
{
top = 10,
left = 20,
width = 200,
height = 30,
cornerRadius = 8,
strokeWidth = 3,
backgroundColor = {1,1,1},
strokeColor = {0,0,0},
font = 'Helvetica',
fontsize = 24,
listener = textFieldHandler
}
)
According to the documentation widget only provides the following function:
https://docs.coronalabs.com/api/library/widget/
widget.newButton()
widget.newPickerWheel()
widget.newProgressView()
widget.newScrollView()
widget.newSegmentedControl()
widget.newSlider()
widget.newSpinner()
widget.newStepper()
widget.newSwitch()
widget.newTabBar()
widget.newTableView()
widget.setTheme()
You can avoid the error message, which I assume is something like Error: Attempt to calla nil value, by simply not calling the nil value widget.newTextField.
Another option is to implement it yourself as described here in detail:
https://coronalabs.com/blog/2013/12/03/tutorial-customizing-text-input/
As this tutorial exactly matches your code I assume you were just to lazy to read beyond the first paragraph...

Using variables in CGRectMake, Swift, UIkit

For an App I'm making i need to use variables to change the size and position of objects (Labels). I've tried var example = CGRectMake(0, 0, 0, 100), hoping it would ignore the zeros (Not really thinking it would though). I then tried:
var example = 100
Label1.frame = CGRectMake(20, 20, 50, example)
I changed the syntax a bit, adding "" and replacing the CGRectMake with CGRect etc, but nothing worked... I don't get what I'm doing wrong here... Help!
Below is the new syntax used since Swift 3.
CGRect(x: 0, y: 0, width: 100, height: 100)
CGRectMake takes CGFloats for all of its arguments. Your sample code should work fine if you specify that example is supposed to be a CGFloat, using a type identifier:
// v~~~~ add this...
var example: CGFloat = 100
Label1.frame = CGRectMake(20, 20, 50, example)
Otherwise, swift infers the type of example to be Int, and the call to CGRectMake fails, cuz it can't take an Int as a parameter...
So, there is many ways to skin the cat. It all depends what your needs and requirements are (maybe you could elaborate a bit on what you are trying to achieve?). But one way to do it could be to set a variable when something happens, and then update the frame of the label. If you added a tap gesture recognizer to your view, and updated your label like so:
let myLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
let tapGestRecog = UITapGestureRecognizer(target: self, action: "handleTap:")
self.view.addGestureRecognizer(tapGestRecog)
}
func handleTap(sender:UIGestureRecognizer) {
let newXposition = sender.locationInView(self.view).x
let newYposition = sender.locationInView(self.view).y
myLabel.frame = CGRectMake(newXposition, newYposition, 200, 200)
}
This is just an example, and a very crude way of doing it. There are many other ways of doing it, but it hopefully gives you an idea of how to achieve it.
Swift allows syntax that Objective-C does not:
var example = 100
label.frame.size.height = example
In objective-C you would have to do it differently:
CGRect frame = label.frame; //Create a temporary rect to hold the frame value
frame.size.height = example;
label.frame = frame;

How to create a variable number of UIImageViews in swift

My problem is that based on the number of groups a person has I want to dynamically create that many UIImageView and assign them to the view. I know how to create a single UIImageView and put it onto the view but how can I create a variable number of them since creating another with the same name seems to just overwrite the original? I tried sending the UIImageView into an Array but got an optional error.
circleView = UIImageView(frame:CGRectMake(125, y, 75, 75))
y+=150
circleView.image = UIImage(named:"circle.png")
circleView.tag = i
self.view.addSubview(circleView)
circleArray[i] = circleView
The problem is that you keep reusing the same circleView instance. When you add it to the array, you are just adding a reference to it. You then reinitialize it, which wipes it out. I also don't see where you are incrementing i. Try something like this:
var circleArray = Array<UIImageView>()
var y : CGFloat = 0.0
var i = 0
circleArray.append(UIImageView(frame:CGRectMake(125, y, 75, 75)))
var circleView = circleArray[i]
circleView.image = UIImage(named:"circle.png")
circleView.tag = i
self.view.addSubview(circleView)
y+=150
i += 1
You need to use circleArray.append(circleView)

Resources