Have UITextField show keyboard (becomeFirstResponder) when UIView is shown - ios4

I have a UIViewController with a UITableView. The first row of the UITableView is a cell that has a UITextField in it. I'd like to improve my UI a little by showing the keyboard when the view is shown. I have tried putting the BecomeFirstResponder method in various events but have yet to get this to work.
Can someone please provide tips on how to present the keyboard when the view is presented via the PushViewController?
Thank you.

In your subclass of UITableViewSource or UITableViewDelegate try overriding WillDisplay method, like this:
public override void WillDisplay (UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
if(indexPath.Row == theRowIndexOfTheCellWithYourTextField){
yourTextField.BecomeFirstResponder();
}
}
It should work (note that you probably wish to add some code to make sure this is executed only once)

First of all, always use a UITableViewController derived class instead of a UIViewController when working with a UITableView. This will help you to resize the view, and makes sure fields are visible when the keyboard is shown.
You can show the keyboard for the first field by calling BecomeFirstResponder in the ViewDidAppear event. Example:
public class YourTableViewController : UITableViewController
{
private UITableView _yourTableView;
YourUITableViewSourceDerivedClass _yourSource;
public override void ViewDidLoad()
{
_yourTableView = new UITableView(View.Bounds, UITableViewStyle.Plain);
_yourTableView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
_yourSource = new YourUITableViewSourceDerivedClass();
_yourTableView.Source = _yourSource;
TableView = _yourTableView;
}
public override void ViewDidAppear(bool animated)
{
// Well, of course you call a method in your source to do this, but this is the idea:
_yourSource.textFieldOnFirstRow.BecomeFirstResponder();
}
}

in your viewdidload, put this:
[yourTextField becomeFirstResponder];
I am not sure at all is this works :S
please tell if it does/don't :)

You can try setting up a notification for when your cell view is loaded or appeared. Then you can call the becomeFirstResponder on the field after the notification is fired off.

Related

Overlay toolbar with other toolbar when item is selected in RecyclerView which is inside a fragment

To illustrate what I mean with this, it is similar to WhatsApp, where various options are displayed in the toolbar when a chat is selected.
I have a similar layout, so a MainActivity with Fragments containing RecyclerViews. Now when an item in a RecyclerView is selected I would like to get a similar behaviour as in WhatsApp. The RecyclerViews have an Adapter that implements an OnClickListener.
However, from this Adapter I do not seem to have access to Views from the MainActivity. I tried the following (inside the OnClick method in the Adapter), but it did not work since the view could not be found.
View view = getActivity().findViewById(R.id.toolbar_main_activity);
if( view instanceof Toolbar) {
Toolbar toolbar = (Toolbar) view;
toolbar.setTitle("TestTitle");
}
Does anyone know how to get the intended behavior or have a reference to a tutorial?
UPDATE: for who is also stuck with this and this is still quite confusing, here is how I solved it in my own words
My Fragment contains the Interface by adding the following code to it;
OnItemsSelected mCallBack;
public interface OnItemsSelected {
void onToolbarOptions(String title);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mCallback = (OnItemsSelected) getActivity();
}
Also I passed 'mCallback' to the adapter like this;
MyAdapter adapter = new MyAdapter(myList, mCallback);
The RecyclerView adapter implements OnClickListener. In the OnClick method I called; 'mCallBack.onToolbarOptions("someTitle");'. And finally I made my MainActivity implement the method; 'implements myFragment.onItemsSelected' and I added the following code to it also;
#Override
public void onToolbarOptions(String title) {
toolbar.setTitle(title);
}
With this, only the title is changed, but from this it is quite easy to make other changes to the toolbar, such as changing the menu items.
Inside your Fragment you make an Interface and a global variable like this:
OnItemsSelected mCallBack;
public interface OnItemsSelected {
public void onToolbarOptions();
}
Then when in your RecyclerView items are selected or clicked you call:
mCallBack.onToolbarOptions();
In your Activity implement the Interface like this plus the method onToolbarOptions():
public static class YourActivityName extends AppCompatActivity
implements YourFragmentName.OnItemsSelected {
public void onToolbarOptions(){
// CHANGE YOUR TOOLBAR HERE
}
//.....OTHER STUFFS IN YOUR ACTIVITY
}

Why isn't my custom UICollectionViewController's GetCell method called

I have a storyboard set up like so:
As you can see, I have a home screen. It has access to a navigation controller. It has two ContainerViews, one used as a sidepane, the other as the main content. I plan to swap things into this main content container as I need them.
One of the things I would like to show in this container, when it is selected from the sidepane, is a CollectionView of cells that show people. Each cell has a photo and the person's details.
The best code example I could find for CollectionView was the Xamarin StateRestoration sample project. It includes a storyboard. I have followed the recipe as best I can while working around my own specific storyboard.
The problem now is that the GetCell(UICollectionView collectionView, NSIndexPath indexPath) method used to populate each cell in the CollectionView is not being called. As far as I know I'm supposed to instantiate the CollectionViewController's Datasource property. I've tried this in AppDelegate and in the ViewDidLoad of my CollectionViewController itself, and GetCell is still not getting called. Why is this?
Any help is appreciated! Ask me to edit if you need more information.
You need to set both datasource and the delegate. As you've mentioned you've set the datasource. To set the delegate open up the storyboard in xcode, select UICollectionView and control drag to the its parent view controller. Have a look at this gif.
Also make sure you implement IUICollectionViewSource interface in your target view controller
public partial class DetailViewController : UIViewController, IUICollectionViewSource
{
protected DetailViewController(IntPtr handle) : base(handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public nint GetItemsCount(UICollectionView collectionView, nint section)
{
return 10;
}
public UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = collectionView.DequeueReusableCell("PersonCell", indexPath) as UICollectionViewCell;
return cell;
}
}
EDIT 1:
You can also do this programmatically. Enter the storyboard, in Widget properties set the name for the UICollectionView - PersonCollection. Then in code behind view controller override ViewDidLoad and set WeakDelegate and WeakDataSource (weak because we definitely not want to create cyclic reference and memory leaks for iOS)
public override void ViewDidLoad()
{
base.ViewDidLoad();
PersonCollection.WeakDataSource = this;
PersonCollection.WeakDelegate = this;
}
Hope this helps!

MonoTouch.Dialog: How to access parent DialogViewController from within Element? Difference between Tapped event and Selected() method?

I'm subclassing a StringElement in MonoTouch.Dialog.
In there I can attach to the Tapped event or I can override Selected().
Both fire if I tap the element.
However, Selected() is giving me access to the DialogViewController the element is a member of, where this information is not passed to the Tapped event.
What is the logic here? Is an element supposed to know its DialogViewController or not? If yes: how to get tho the controller from the Tapped event then?
Found out myself by looking at the source on Github.
The only place where Tapped event is triggered, is from Selected(). So I think Tapped should really by of type EventHandler instead of Action.
public override void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath indexPath)
{
if (Tapped != null)
Tapped ();
tableView.DeselectRow (indexPath, true);
}
At the time I wrote that code, the idea was simply that with lambdas, you can pass whatever state you need to your Tapped handler, without using the object/EventArgs pattern.
So you would do something like:
var dialogViewController = CreateDvC ();
new StringElement ("....", () => {
// reference any variables here
// my container is:
Console.Writeline (dialogViewController);
}

Missing Reference to Navigation Controller in Monotouch

I am new to Monotouch and attempting to understand how some of the basics hang together. Hopefully someone out there will be able to assist.
I've created a test project in MonoDevelop based on the Multi-Screened Apps tutorial on the Xamarin site and have extended it to include a tableView. I am having issues with referencing the Navigation Controller in a view that I need to push a detail view onto to display the detail of an item tapped in the table via an accessory button. I know some of the coding is scrappy, just been trying to get it working at this stage rather than the clarity in the code! (I'm using the latest versions of all Mono tools/libraries etc and XCode 4 on Lion). Starting at the beginning here's the code in FinishedLaunching in AppDelegate.
window = new UIWindow (UIScreen.MainScreen.Bounds);
this.rootNavigationController = new UINavigationController();
// Create a new homescreen
HomeScreen homeScreen = new HomeScreen();
// Add the homescreen to the root navigation controller
this.rootNavigationController.PushViewController(homeScreen, false);
// Set the root view controller on the window - the navigation controller
// will handle the rest.
this.window.RootViewController = this.rootNavigationController;
// make the window visible
this.window.MakeKeyAndVisible ();
homeScreen just contains a button which loads a new view containing a table view (OrderList). Here's the button event handler.
void HandleOrdersButtonhandleTouchUpInside (object sender, EventArgs e)
{
if (orderListScreen == null)
orderListScreen = new OrderList();
NavigationController.PushViewController(orderListScreen, true);
}
This all works fine. I've got some dummy data that loads into the table view, which also works fine. OrderData is a simple class for testing which just contains a couple of properties. I've added an AccessoryButton to the cells and am trying to load a detail view when this is tapped. Here's the code that does this - comment in code where issue is! (I'd previously tested the AccessoryButtonTapped functionilty was working by just displaying an alert).
public override void AccessoryButtonTapped (UITableView tableView, NSIndexPath indexPath)
{
var dataSource = (OrdersTableViewDataSource)tableView.DataSource;
if (detailScreen == null)
detailScreen = new OrderDetailScreen();
OrderData theOrder = dataSource.OrdersData[indexPath.Row];
detailScreen.currentOrder = theOrder;
// Cant get a reference to NavigationController here to push the detail view!
// this.NavigationController is not available
this.NavigationController.PushViewController(detailScreen, true);
}
My understanding of NavigationControllers from what I've read so far is that this reference should be available through all views that originate from the root ViewController/NavigationController without the need to pass the reference from AppDelegate through the various view constructors?
Can anyone tell me what I might be missing here?
Thanks in advance.
** An update after reviewing Jason's comment: (Please let me know if this is the incorrect way to post updates)
So, I tried the following:
I saved a reference to the NavigationController in the constructor for the ViewController that contains the table view as follows:
public partial class OrderList : UIViewController
{
UINavigationController navController;
public OrderList () : base ("OrderList", null)
{
this.Title = "Orders";
navController = this.NavigationController;
}
Then passed that into the TableViewDelegate, where the AccessoryButtonTapped is handled, in the ViewDidLoad method.
public override void ViewDidLoad ()
{
orderTableView.DataSource = new OrdersTableViewDataSource();
orderTableView.Delegate = new OrdersTableViewDelegate(navController);
base.ViewDidLoad ();
}
Then referenced that in the TableViewDelegate:
public class OrdersTableViewDelegate : UITableViewDelegate
{
UINavigationController navController;
public OrdersTableViewDelegate(UINavigationController controller)
{
navController = controller;
}
// Rest of class definition
}
Then the reference to the NavigationController using navController compiles with the code as previously described using the following in the AccessoryButtonTapped method:
navController.PushViewController(detailScreen, true);
When I run this and tap on the AccessoryButton I get a null reference exception on navController. The reference to this.NavigationController in the ViewController constructor is null. Am I doing something in the wrong place or sequence?
Cheers
The NavigationController property is on your table's view controller. If you are trying to reference it from your table's datasource, you need to pass a reference to the controller when you create the datasource.

Why would MonoTouch not garbage collect my custom UIButton unless I call button.RemoveFromSuperview()?

There seems to be something holding a reference to my custom button, MyButton (which inherits from UIButton), causing it not to be garbage collected unless I remove it from the superview. This, in turn, would cause the view controller that it is on to also not be finalized and collected.
In my example, I have my custom button but I also have a standard UIButton on the view controller which does not need to be removed from the superview in order to be collected. What's the difference? Looks pretty similar to me.
See this code. The irrelevant lines were removed for example's sake. Some things to note about the sample:
-MyButton is pretty empty. Just a constructor and nothing else overridden.
-Imagine MyViewController being on a UINavigationController
-LoadView() just creates the buttons, hooks up an event for each and adds it to the view
-Touching _button would push another MyViewController to the nav controller
-I'm doing some reference cleanup when popping view controllers off the nav controller in ViewDidAppear()
-In CleanUpRefs() you'll see that I have to remove _myButton from superview in order for all the objects to be garbage collected. _button, on the other hand does not need to be removed.
-I'm expecting the entire MyViewController to be collected, including all subviews, when popping from the nav controller but commenting out _myButton.RemoveFromSuperview() stops this from happening.
public class MyViewController : UIViewController
{
private UIButton _button;
private MyButton _myButton;
private MyViewController _nextController;
public override void LoadView()
{
base.LoadView();
_button = UIButton.FromType(UIButtonType.RoundedRect);
_button.TouchUpInside += PushNewController;
View.AddSubview(_button);
_myButton = new MyButton();
_myButton.TouchUpInside += MyButtonTouched;
View.AddSubview(_myButton);
}
private void PushNewController(object sender, EventArgs e)
{
_nextController = new MyViewController();
NavigationController.PushViewController(_nextController, true);
}
private void MyButtonTouched(object sender, EventArgs e)
{
Console.WriteLine("MyButton touched");
}
public void CleanUpRefs()
{
//_button.RemoveFromSuperview();
_myButton.RemoveFromSuperview();
// remove reference from hooking up event handler
_button.TouchUpInside -= PushNewController;
_myButton.TouchUpInside -= MyButtonTouched;
_button = null;
_myButton = null;
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
if(_nextController != null)
{
_nextController.CleanUpRefs();
_nextController = null;
}
}
}
It seems as if there's something different with the fact that MyButton isn't a straight UIButton in that it is inherited. But then again, why would there be an extra reference count to it that's being removed by calling RemoveFromSuperview() especially when there's a UIButton just like it that doesn't need to be removed?
(I apologize for the really bad layout, stackoverflow seems to have problems laying out bullets right above code snippets)
Update: I filed a bug report with the MonoTouch team. You can download the sample project from there if you want to run it. Bug 92.
The reason for not garbage collecting in that scenario is just a bug in MonoTouch.
The upcoming MonoTouch release will contain a fix for this. If you are in a hurry, you can replace your /Developer/MonoTouch/usr/lib/mono/2.1/monotouch.dll with the copy I placed here:
http://tirania.org/tmp/monotouch.dll
I would make a backup, in case I did something wrong in my work-in-progress library.

Resources