I have a simple UIViewController (in monotouch), with a button on the UI. Now, my use case is simple - I want to reload the UIViewController whenever the button is clicked. On some threads i read that i just need to call SetNeedsDisplay for the UIView, but that doesn't seem to work for me. Here is my code snippet -
public partial class AbcScreen : UIViewController
{
....
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Declare and Add a button
Add(myBtn);
myBtn.TouchUpInside += (sender, ea) =>
{
Console.WriteLine("Touched the button");
View.SetNeedsDisplay();
};
}
}
I read some other threads, but nothing seems to help. Please help! Thanks!
Try with
this.ReloadInputViews();
Related
I tried to call a IEnumerator function from button click so I could yield return something, but I can't choose the function from the inspector's On Click() dropdown menu.
I tried to call the IEnumerator from another function and assign that function to the button, doesn't work!
And apparently I can't do yield return in a void function.
So could somebody please be so kind and teach me what should I do!?
Much appreciated!
There are a certain rules to hook a function to a unity event from the inspector.
1) The function must be public to be able to choose it from the inspector.
2) The return type must be void, so you won't be able to choose functions with any return type, and since your IEnumerator function returns an IEnumerator, you won't be able to choose it from the inspector.
3) The function parameters must match the event parameters unless the UnityEvent doesn't take any parameters like Button.onClick event (thanks #derHugo for correcting), for example the button onClick event doesn't take any parameters, so to choose a function for it from the inspector the function must look like
public void ChoosableTemplateForOnClickButton() { }
But you want to call an IEnumerator from the inspector, so what you can do is wrap it in a void function:
assuming your IEnumerator function looks like
public IEnumerator MyRoutine()
{
yield and enjoy...
}
wrap it in a void function:
public void RoutineWrap()
{
StartCoroutine(MyRoutine());
}
Now you can choose RoutineWrap from the inspector.
Alternative to this correct answer you can always also add the callback on runtime via script.
Still in Unity every Coroutine has to be started using StartCoroutine:
public Button button;
private void Awake()
{
button.onClick.AddListener(() => StartCoroutine(MyRoutine()));
}
private IEnumerator MyRoutine()
{
...
}
I have a View 1 (UIViewController) which has a button to navigate to a new view - View 2 (UIViewController).
I would like to refresh View 1 when user clicks back button from View 2. UIViewController does not have ViewWillAppear to know the control is back on the view.
How to determine the control is passed on that view
You can override ViewDidAppear to detect that View 1 is visible again and refresh the view. Do not forget to call the base method too.
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear (animated);
//Refresh view here.
}
UIViewController does not have ViewWillAppear
You don't have the method ViewWillAppear on an UIViewController?
You could try to use the UINavigationControllerDelegate and specifically WillShowViewController
1)We can set it with a bool Value.
bool viewLoadedAgain = false;
2)Bool value gets 'true' when segue triggers:
public override void PrepareForSegue (UIStoryboardSegue segue, Foundation.NSObject sender)
{
base.PrepareForSegue (segue, sender);
if(segue.Identifier == "segueToSecondVC")
{
viewLoadedAgain = true;
}
}
3) Perform Refreshing ViewController View.
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
this.PerformSegue ("segueToSecondVC",this);
if(viewLoadedAgain == true)
{
//Use any code as per requirement
this.View.SetNeedsLayout ();
this.View.SetNeedsUpdateConstraints ();
this.View.ReloadInputViews ();
this.View.SetNeedsDisplay ();
}
}
I have a viewcontroller that hold a button for now, and I want to create a new viewcontroller and add his view over the current one
So far I did this:
class ViewController : UIViewController
{
var myController: MyController = MyController.controller()
init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!)
{
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
override func viewDidLoad()
{
super.viewDidLoad()
self.view.addSubview(self.myController.view)
}
#IBAction func buttonPressed(sender : AnyObject)
{
}
}
in MyController I did a singleton (I hope I did it right)
class MyController : UIViewController
{
init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!)
{
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
class func controller() -> MyController
{
var once : dispatch_once_t = 0
var sharedInstance: MyController?
dispatch_once(&once, { sharedInstance = MyController()})
return sharedInstance!
}
}
the behavior I got is this, if I DON'T call the
self.view.addSubview(self.myController.view)
everything works fine, if I did, the button in the center is not anymore recognizing the touches, it is like it has something over that intercept the touch, what is wrong here? what I'm missing?
in MyController I did a singleton (I hope I did it right)
You did it wrong. Singleton is not required here. If you are using storyboard, the below line
var myController: MyController = MyController.controller()
could be
var myController: MyController = self.storyboard.instantiateViewControllerWithIdentifier("MyViewController")
Then before adding sub view set the frame of the self.myController.view
self.myController.view.frame = CGRectMake(0, 0, 320, 200); // or what ever frame you want
I'm building a panel of buttons, that are enclosed in Canvas container. For this purpose I created a MyButton class, which is a subclass of UIComponent. MyButton class has 2 other subclasses: MyRadioButton and MyMenuButton, which have another behavior on MouseEvent.MOUSE_DOWN. MyMenuButtoncreates and shows a menu, that I build from XML and it builds ok.
I add listeners in the superclass like this:
this.addEventListener(MouseEvent.CLICK, handleClick);
this.addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
this.addEventListener(MouseEvent.MOUSE_UP, handleMouse);
It's done at the creation stage.
In my subclasses I override handleMouse handler.
override protected handleMouse(event:MouseEvent) : void
{
// subclass specific handling
}
These button objects are added to canvas container as following:
in class MyButtonsContainer.as:
this.rowChildren.addChild(button);
These buttons draw perfectly and in place. The problem is the behavior:
The event doesn't come to handleClick handler of superclass. And that's the question actually - why can it be?
Thanks in advance
EDIT: It appears that MOUSE_DOWN & MOUSE_UP interfere with CLICK event. When I remove listeners to them I get to click handler.How to cause them live together?
I managed to catch CLICK event as timing difference between MOUSE_DOWN & MOUSE_UP.
EDIT: A short/long explanation: For some reason my listeners for MOUSE_DOWN/MOUSE_UP events were interfering with MouseEvent.CLICK. I was suggested by somebody to give up on listening to MouseEvent.CLICK and instead start a timer in MouseEvent.MOUSE_DOWN and check again the timer in MouseEvent.MOUSE_UP. If the difference less than 0.1 (you can put there another threshold) then it was a click actually.
Now some sample code:
public class MyButton extends UIComponent
{
...
private var _clickTime : Number = 0.;
public function void MyButton()
{
super();
addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
addEventListener(MouseEvent.MOUSE_UP, handleMouse);
}
protected function checkForClick(event:MouseEvent) : Boolean
{
var down : Boolean = event.type == MouseEvent.MOUSE_DOWN;
if (down)
_clickTime = getTimer();
else {
var diff : Number = getTimer() - _clickTime;
if (diff/1000. < 1.) {
handleClick();
return true;
}
}
return false;
}
protected function handleClick(event:MouseEvent) : void
{
// handle the click here
}
protected function handleMouse(event:MouseEvent) : void
{
checkForClick(event);
...
// take care of your button graphic state
}
DISCLAIMER: This code works for me and good for me and my application needs. If you disagree with this way or have a better way to suggest, please do it in comments. Since this answer answers my own question and this EDIT was added only because I was asked to do so, please don't downvote it if you don't agree with it.
call the superclass method like so
override protected handleMouse(event:MouseEvent) : void
{
super.handleMouse(event);
// subclass specific handling
}
If I'm understanding you correctly, you just need to make sure that in your overriden handlers you end each one with a call to super -
override protected handleMouse(event:MouseEvent):void {
// my logic here for the subclass
trace("blah blah blah");
//now redispatch up to the superclass.
super.handleMouse(event);
}
I'm not 100% sure what you're asking, but I'd override click actions like this..
Base class:
public class MyButton extends UIComponent
{
/**
* Constructor
*/
public function MyButton()
{
// listeners
addEventListener(MouseEvent.CLICK, _click);
// ...other listeners
}
/**
* Called on dispatch of MouseEvent.CLICK
*/
private function _click(e:MouseEvent):void
{
doClick();
}
/**
* Override this method and fill with actions to
* do when this is clicked
*/
protected function doClick():void
{
trace("base class was clicked");
}
}
And the extending class:
public class MyOtherButton extends MyButton
{
/**
* Override doClick
*/
override protected function doClick():void
{
super.doClick();
trace("extending class was clicked");
}
}
After closely inspecting your code:
You've added two different listeners to one function for one, shouldn't these have seperate functions as they are called by contrasting events?
this.addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
this.addEventListener(MouseEvent.MOUSE_UP, handleMouse);
Also, it seems like you're expecting handleMouse to be fired by a click event judging by the below code, which isn't the case.
override protected handleMouse(event:MouseEvent) : void
{
// subclass specific handling
}
Either that, or you're forgetting to override handleClick as well - which is the method that you're running on a click event. You possibly need to add this code:
override protected handleClick(event:MouseEvent) : void
{
// subclass specific handling for a click
}
I'm trying to put html-formatted labels in the tabs of a TabNavigator.
I saw SuperTabNavigator in the FlexLib but it doesn't seem to do the trick for me.
I found this html button code and was able to inject my own TabBar and have it change the class instantiated by the ClassFactory when a navItem is created.
HtmlTabNavigator:
public class HtmlTabNavigator extends TabNavigator
{
public function HtmlTabNavigator()
{
super();
}
override protected function createChildren():void
{
if (!tabBar)
{
tabBar = new HtmlTabBar(); // inject my class
tabBar.name = "tabBar";
tabBar.focusEnabled = false;
tabBar.styleName = new StyleProxy(this, tabBarStyleFilters);
rawChildren.addChild(tabBar);
if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0)
{
tabBar.setStyle("paddingTop", 0);
tabBar.setStyle("paddingBottom", 0);
tabBar.setStyle("borderStyle", "none");
}
}
super.createChildren(); // ommits original TabBar creation but continues in inheritance chain
}
public function setHtmlLabels( htmlLabels:Array ):void
{
for (var i:uint = 0; i < tabBar.numChildren; i++)
{
var button:Button = tabBar.getChildAt( i ) as Button;
button.label = htmlLabels[ i ];
}
}
}
HtmlTabBar:
public class HtmlTabBar extends TabBar
{
public function HtmlTabBar()
{
super();
navItemFactory = new ClassFactory(HtmlButton);
}
}
Now I'm having problems with the style of the button as it looks like a regular button and not like a tab anymore. It is not apparent to me why this works when a ButtonBarButton is used.
Any ideas are welcome.
Thanks
Stefan
Like you mentioned, I think its best to built your own component for this scenario. You could consider using a textArea with editable=false for the tab portion since I believe htmlText can be displayed in that component.
Edit: Can you maybe modify the SuperTabNavigator and add in a textArea... so the original label for the tab could be blank (if you cant remove it) then have the textArea on top of it.
With best regards,
Following your example, I tried to specialize the TabBar to use a Custom Button with a Mnemonic Label functionality, but I get a compile time error:
1178: Attempted access of inaccessible
property navItemFactory through a
reference with static type
AspMnemonicTabBar.
Reading the Flex Source Code I found it belongs to a private namespace mx_internal. So how could I access it to set a new ClassFactory ??
package
{
import mx.controls.TabBar;
import mx.core.ClassFactory;
public class AspMnemonicTabBar extends TabBar
{
public function AspMnemonicTabBar()
{
super();
navItemFactory = new ClassFactory(AspMnemonicButton);
}
}
}
Alessandro, navItemFactory is in namespace mx_internal. Access it through mx_internal::navItemFactory, or put the following line below your import statements:
use namespace mx_internal;