I'm using a UITableViewController.
Instead of displaying an alert view I want to be redirected to another page after i touch a particular row in a table.
I have this code but it doesn't work
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
new UIAlertView("Row Selected", tableItems[indexPath.Row], null, "OK", null).Show();
tableView.DeselectRow (indexPath, true); // normal iOS behaviour is to remove the blue highlight
//call the next screen
if(this.accountRegistration== null) {
this.accountRegistration = new AccountRegistration();
}
this.NavigationController.PushViewController(this.accountRegistration, true);
}
I'm changing my answer because I didn't realize you were using a UITableViewController.
In your AppDelegate.cs file you need to set the rootNavigationController to a new UINavigationController:
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
// class-level declarations
UIWindow window;
UINavigationController rootNavigationController;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
this.window = new UIWindow (UIScreen.MainScreen.Bounds);
//---- instantiate a new navigation controller
this.rootNavigationController = new UINavigationController();
this.rootNavigationController.PushViewController(new MyUITableViewController(), false);
//---- set the root view controller on the window. the nav
// controller will handle the rest
this.window.RootViewController = this.rootNavigationController;
this.window.MakeKeyAndVisible ();
return true;
}
....
From that point on, your UITableViewController should always have a reference to this.NavigationController.
This code works well
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
// class-level declarations
UIWindow window;
UINavigationController rootNavigationController;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
this.window = new UIWindow (UIScreen.MainScreen.Bounds);
//---- instantiate a new navigation controller
this.rootNavigationController = new UINavigationController();
this.rootNavigationController.PushViewController(new MyUITableViewController(), false);
//---- set the root view controller on the window. the nav
// controller will handle the rest
this.window.RootViewController = this.rootNavigationController;
this.window.MakeKeyAndVisible ();
return true;
}
Related
I'm trying to create my first iOS project. I have quite well experiences with C#. I receive data with categories and articles. But I don't know, how many subcategories I will get, before the articles will be listed. The browsing though the subcategories should be done via TableView. When a User clicks a category, the subcategories (level 1) should be displayed in a tableView. Then, after touching a subcategory, the subcategories (level 2) of present should be displayed (and so on). Lastly, when there are no further subcategories, the article-list should be displayed. After touching one article, a new ViewController with the article-data should be displayed.
My question is, how to handle the navigation through the subcategories and creating segues or sth. like that. I know, that I can't use storyboard for this, because I don't know the number of subcategories and they also differ.
How can I achieve this dynamic navigation? Can you give me an example? I know how to populate data to the tableView. Only the dynamic navigation is the problem.
I now have a solution. Can someone please check, if I am doing it correctly not that I don't violate programming patterns or guidelines.
In short: In TableSource-Class I access the Storyboard and dynamically create and show the same Controller which actually is presented. But before presenting the new one, I declare an other source-class. So the TableView is used for displaying Addressbooks and Addresscards, the UIViewController is used for displaying the carddata. These two controllers are connected via segue.
Here are my Controller and both Source-Classes:
AddressbooksController.cs
public AddressbooksController (IntPtr handle) : base (handle)
{
displayModel = "addressbooks";
}
private void loadAddressbooks()
{
var AppDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;
addressbooks = AppDelegate.api.DeserializeEntities<YAddressbook>(Task.Run(async () => await AppDelegate.api.Get("Yaddressbooks")).Result);
}
public void loadAddresscards()
{
var AppDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;
addresscards = AppDelegate.api.DeserializeEntities<Ycard>(Task.Run(async () => await AppDelegate.api.Get("Ycards")).Result);
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue(segue, sender);
// do first a control on the Identifier for your segu
if (segue.Identifier.Equals("segueShowAddress"))
{
var viewController = (AddresscardController)segue.DestinationViewController;
viewController.card = Card2Display;
}
}
public override void ViewDidLoad()
{
if (displayModel == "addressbooks")
{
loadAddressbooks();
tableView.Source = new AddressbooksTableSource(addressbooks.data, this);
this.NavigationController.Title = "Addressbücher";
}
else if (displayModel == "addresscards")
{
loadAddresscards();
tableView.Source = new AddresscardsTableSource(addresscards.data, this);
this.NavigationController.
}
base.ViewDidLoad();
}
AddressbooksTableSource
public class AddressbooksTableSource: UITableViewSource
{
private List<YAddressbook> addressbooks;
private string cellIdentifier = "AddressbooksCell";
private UINavigationController navigationController;
public AddressbooksTableSource(List<YAddressbook> books, AddressbooksController ab)
{
addressbooks = books;
this.navigationController = ab.ParentViewController as UINavigationController;
Console.WriteLine(ab.displayModel);
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
Console.WriteLine("Row selected" + addressbooks[indexPath.Row].displayname);
UIStoryboard Storyboard = UIStoryboard.FromName("Main", null);
AddressbooksController newab = Storyboard.InstantiateViewController("AddressbooksViewController") as AddressbooksController;
newab.displayModel = "addresscards";
navigationController.PushViewController(newab, true);
tableView.DeselectRow(indexPath, true);
}
....
}
AddresscardsTableSource
public class AddresscardsTableSource: UITableViewSource
{
private List<Ycard> addresscards;
UINavigationController navigationController;
AddressbooksController ab;
string cellIdentifier = "AddresscardCell";
public AddresscardsTableSource(List<Ycard> cards, AddressbooksController vc)
{
addresscards = cards;
navigationController = vc.ParentViewController as UINavigationController;
ab = vc;
//navigationController = tableview;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
Console.WriteLine("Row selected" + addresscards[indexPath.Row].carddata);
//UIStoryboard Storyboard = UIStoryboard.FromName("Main", null);
ab.Card2Display = addresscards[indexPath.Row];
ab.PerformSegue("segueShowAddress", indexPath);
//AddresscardController ab = Storyboard.InstantiateViewController("AddresscardViewController") as AddressbooksController;
//ab.TableView.Source = this;
//navigationController.PushViewController(ab, true);
tableView.DeselectRow(indexPath, true);
}
.....
}
It works. But am I doing it correctly? Thanks
Is it possible to hide a status bar in a PageRenderer?
I tried setting these in Info.plist
<key>UIStatusBarHidden</key>
<true/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
And then overriding PrefersStatusBarHidden in the PageRenderer as
public override bool PrefersStatusBarHidden()
{
return true;
}
If I set UIViewControllerBasedStatusBarAppearance in Info.plist to false, it is hidden on all pages.
How can I fix this?
You should use a custom UINavigationController instead of the system one, like this sample:
In AppDelegate.cs:
UINavigationController navController;
UIWindow window;
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
navController = new UINavigationController (new TestViewCtonroller ());
// create a new window instance based on the screen size
window = new UIWindow (UIScreen.MainScreen.Bounds);
window.RootViewController = navController;
window.MakeKeyAndVisible ();
return true;
}
MyNavigationController.cs:
public class MyNavigationController : UINavigationController
{
public MyNavigationController (UIViewController rootController) : base(rootController)
{
}
public override UIViewController ChildViewControllerForStatusBarHidden ()
{
return TopViewController;
}
public override UIViewController ChildViewControllerForStatusBarStyle ()
{
return TopViewController;
}
}
TestViewCtonroller.cs:
public class TestViewCtonroller : UIViewController
{
public TestViewCtonroller ()
{
this.View.BackgroundColor = UIColor.Red;
}
public override UIStatusBarStyle PreferredStatusBarStyle ()
{
return UIStatusBarStyle.LightContent;
}
public override bool PrefersStatusBarHidden ()
{
return true;
}
}
Hope it can help you.
When loading the sample code in Xamarin Studio, the app runs as expected.
Xamarin.com Local Notifications Sample Code
But when starting a new single view project, I tried using bits of the code that seemed necessary. The viewcontroller class is structured differently, than in the sample code, on a new project.
ViewController.cs
using System;
using UIKit;
using Foundation;
namespace TestProject1
{
public partial class ViewController : UIViewController
{
public ViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void DidReceiveMemoryWarning ()
{
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
partial void ButButton_TouchUpInside (UIButton sender)
{
var notification = new UILocalNotification();
notification.FireDate = NSDate.FromTimeIntervalSinceNow(5);
notification.AlertAction = "Test";
notification.AlertBody = "Test Text";
notification.ApplicationIconBadgeNumber = 1;
notification.SoundName = UILocalNotification.DefaultSoundName;
UIApplication.SharedApplication.ScheduleLocalNotification(notification);
//throw new NotImplementedException ();
}
}
}
AppDelegate.cs
using Foundation;
using UIKit;
namespace TestProject1
{
[Register ("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
private ViewController viewController;
private UIWindow window;
public override UIWindow Window {
get;
set;
}
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
viewController = new ViewController();
window = new UIWindow (UIScreen.MainScreen.Bounds);
viewController = new ViewController ();
window.RootViewController = viewController;
window.MakeKeyAndVisible ();
//if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
var notificationSettings = UIUserNotificationSettings.GetSettingsForTypes (
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, null
);
application.RegisterUserNotificationSettings (notificationSettings);
//}
if (launchOptions != null)
{
// check for a local notification
if (launchOptions.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey))
{
var localNotification = launchOptions[UIApplication.LaunchOptionsLocalNotificationKey] as UILocalNotification;
if (localNotification != null)
{
UIAlertController okayAlertController = UIAlertController.Create (localNotification.AlertAction, localNotification.AlertBody, UIAlertControllerStyle.Alert);
okayAlertController.AddAction (UIAlertAction.Create ("OK", UIAlertActionStyle.Default, null));
viewController.PresentViewController (okayAlertController, true, null);
// reset our badge
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
}
}
}
return true;
}
public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
{
// show an alert
UIAlertController okayAlertController = UIAlertController.Create (notification.AlertAction, notification.AlertBody, UIAlertControllerStyle.Alert);
okayAlertController.AddAction (UIAlertAction.Create ("OK", UIAlertActionStyle.Default, null));
viewController.PresentViewController (okayAlertController, true, null);
// reset our badge
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
}
}
}
In the sample project, the only difference is ViewController is declared with no parameters: public ViewController { }. If I add that code, the app complies and runs. The notifications fires and shows the badge, but never shows appears within the app.
Instead of trying to rig the code on a new project, how do you properly declare: viewController = new ViewController(); with a IntPtr parameter?
Thanks in advance!
Instead of trying to use attach the alert to the ViewController as shown in the demo code, use UIAlertView.
public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
{
UIAlertView alert = new UIAlertView () { Title = notification.AlertAction, Message = notification.AlertBody };
alert.AddButton("OK");
alert.Show ();
// CLEAR BADGES
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
}
Consider this simple example:
public partial class TableViewController : UITableViewController
{
public TableViewController (IntPtr handle) : base (handle)
{
}
protected override void Dispose (bool disposing)
{
Console.WriteLine (String.Format ("{0} controller disposed - {1}", this.GetType (), this.GetHashCode ()));
base.Dispose (disposing);
}
public override void ViewDidLoad ()
{
//TableView.Source = new TableSource(this);
TableView.Source = new TableSource();
}
}
public class TableSource : UITableViewSource {
private TableViewController controller;
string CellIdentifier = "TableCell";
public TableSource ()
{
}
public TableSource (TableViewController controller)
{
this.controller = controller;
}
public override nint RowsInSection (UITableView tableview, nint section)
{
return 1;
}
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell (CellIdentifier);
//if there are no cells to reuse, create a new one
if (cell == null){
cell = new UITableViewCell (UITableViewCellStyle.Default, CellIdentifier);
}
cell.TextLabel.Text = "test";
return cell;
}
}
I've noticed that the view controller (TableViewController) is never released. The table view controller has a reference to the data source, but the data source also has a reference to the table view controller.
With TableView.Source = new TableSource(); the view controller gets released, with TableView.Source = new TableSource(this); it's not.
How should this reference cycle be broken so that everything get released?
Edit:
Now I tried the WeakReference:
Through using a WeakReference the Dispose method is called, when the view controller is popped off the navigation stack.
In ViewDidLoad:
TableView.Source = new TableSource(new WeakReference<TableViewController> (this));
In the datasource:
private WeakReference<TableViewController> controller;
public TableSource (WeakReference<TableViewController> controller)
{
this.controller = controller;
}
I built this into my real project, but how can I access my controller? I get the message
Type 'System.WeakReference' does not contain a definition for 'xxx' and no extension method 'xxx' of type 'System.WeakReference' could be found. Are you missing an assembly reference?
You work with Xamarin, as I see? Have you tried WeakReference?
https://msdn.microsoft.com/en-us/library/system.weakreference(v=vs.110).aspx
PS:
private WeakReference weakController;
to set:
this.weakController = new WeakReference(controller);
to get:
if (weakController.isAlive)
{
TableViewController controller = weakController.Target as TableViewController;
}
change
public partial class TableViewController : UITableViewController
to
public partial class TableViewController : UITableViewController, UITableViewSource
and in ViewDidLoad just do
self.TableView.Source = self;
the source property is a weak reference internally already so your have no problem of managing that. It is a convenience property to make the tbaleviewcontroller the delegate and datasource altogether. (Just like you would in native iOS)
You can move the methods into the controller itself which would be less of a hassle than WeakReference. Then mark them with export attribute which then allows you to set the UITableView.WeakDataSource property to the controller itself.
[Export("tableView:numberOfRowsInSection:")]
public nint RowsInSection (UITableView tableview, nint section)
[Export("tableView:cellForRowAtIndexPath:")]
public UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
Once they are moved you can attach the datasource:
public override void ViewDidLoad ()
{
TableView.WeakDataSource = this;
}
How can I access NavigationController inside UITableviewSource class?
On Row selection I want to navigate to another UIController.
This is my code,
public class RootTableSource : UITableViewSource
{
IList<VendorDetails> tableItems;
string cellIdentifier = "UIViewController";
ReportsList reportList;
AddNewReport addnewReport;
public RootTableSource()
{
}
public RootTableSource (IEnumerable<VendorDetails> items)
{
tableItems = items.ToList ();
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow (indexPath, true);
// Redirect to another UIController....
}
public VendorDetails GetItem (int id)
{
return tableItems [id];
}
}
have you instance UINavigationController in AppDelegate ?
like this...
#property (strong, nonatomic) UINavigationController *navigationViewController;
then you should can access UINavigationController from AppDelegate delegate
#import "AppDelegate.h"
#implementation yourClassName
-(void)functionName{
AppDelegate *appdelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
appdelegate.navigationViewController; //your UINavigationController
[appdelegate.navigationViewController pushViewController:yourUIViewController animated:YES];
}
#end
wish help ~
edit:
Sorry i have never use Xamarin before
so i think this is a bad way to implement ...
but it look work
AppDelegates.cs
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
HomeScreen home;
public static UINavigationController navigation;
//set navigation public and static
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
home = new HomeScreen ();
navigation = new UINavigationController(home);
window.RootViewController = navigation;
window.MakeKeyAndVisible ();
return true;
}
}
yourClassName.cs
AppDelegate.navigation
//access navigation
wish help again ...
The way I do this is by passing a reference to the TableViewController when I create my Source. Then the Source can use this reference to access it's parent's NavigationController.
UITableViewController _parent;
public RootTableSource (IEnumerable<VendorDetails> items, UITableViewController parent)
{
tableItems = items.ToList ();
_parent = parent;
}
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow (indexPath, true);
_parent.NavigationController.PushViewController(...);
}