Xamarin iOS black screen - c#

I´m very new to Xamarin and currently I want to develop a little iOS app that receives data from a REST-Service.
So I have the following Storyboard:
In the table view there should be some entries that are consumed from the REST-Service. When you click on one row you should be "redirected" to a detail view which should be the right View.
The problem is, that the screen of the right story board is just black:
Well let´s have a look at my code, first the FinishedLaunching method in my AppDelegate.cs:
public override void FinishedLaunching(UIApplication application)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
UIStoryboard storyboard;
UIViewController initialViewController;
var credential = CredentialController.Instance.FindAccount(CustomStaticLiterals.AppName);
if (credential == null)
{
storyboard = UIStoryboard.FromName("AccountCreationViewController", NSBundle.MainBundle);
initialViewController = storyboard.InstantiateInitialViewController() as UIViewController;
}
else
{
storyboard = UIStoryboard.FromName("OverviewViewController", NSBundle.MainBundle);
initialViewController = storyboard.InstantiateInitialViewController() as UIViewController;
}
Window.RootViewController = initialViewController;
Window.MakeKeyAndVisible();
}
Here is my ProjectOverviewController:
public partial class ProjectOverviewController : UITableViewController
{
public List<Project> Projects { get; set; }
private static readonly NSString callHistoryCellId = new NSString("ProjectCell");
public ProjectOverviewController(IntPtr handle) : base(handle)
{
TableView.RegisterClassForCellReuse(typeof(UITableViewCell), callHistoryCellId);
ProjectOverviewDataSource source = new ProjectOverviewDataSource(this);
source.DetailProjectEvent += DetailProjectPage;
TableView.Source = source;
Projects = new List<Project>();
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
var credentials = CredentialController.Instance.FindAccount(CustomStaticLiterals.AppName);
var service = new ServiceInvoker();
Projects = new List<Project>(service.GetCurrentProjectsOfUserAsync(credentials.UserName).Result);
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
}
void DetailProjectPage(object sender, EventArgs e, int index)
{
var currentProject = Projects[index];
var projectViewController =
Storyboard.InstantiateViewController("ProjectViewController") as ProjectViewController;
if (projectViewController != null)
{
projectViewController.Initialize(currentProject);
NavigationController.PushViewController(projectViewController, true);
}
}
class ProjectOverviewDataSource : UITableViewSource
{
ProjectOverviewController controller;
public delegate void DetailProjectHandler(object sender, EventArgs e, int index);
public event DetailProjectHandler DetailProjectEvent;
public ProjectOverviewDataSource(ProjectOverviewController controller)
{
this.controller = controller;
}
// Returns the number of rows in each section of the table
public override nint RowsInSection(UITableView tableView, nint section)
{
return controller.Projects.Count;
}
//
// Returns a table cell for the row indicated by row property of the NSIndexPath
// This method is called multiple times to populate each row of the table.
// The method automatically uses cells that have scrolled off the screen or creates new ones as necessary.
//
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell(ProjectOverviewController.callHistoryCellId);
int row = indexPath.Row;
cell.TextLabel.Text = controller.Projects[row].Header;
return cell;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
var index = indexPath.Row;
if (DetailProjectEvent != null)
{
DetailProjectEvent(this, new EventArgs(), index);
}
tableView.DeselectRow(indexPath, true);
}
}
}
And finally the the ProjectViewController:
public sealed partial class ProjectViewController : UIPageViewController
{
private Project masterModel;
public ProjectViewController (IntPtr handle) : base (handle)
{
}
public void Initialize(Project masterModel)
{
if (masterModel == null)
{
throw new ArgumentNullException(nameof(masterModel));
}
this.masterModel = masterModel;
}
public override void ViewDidLoad()
{
Title = masterModel.Header;
CustomerField = new UITextField();
CustomerField.Text = masterModel.Customer;
ObjectAddressField = new UITextField();
ObjectAddressField.Text = masterModel.Customer;
}
}
Well there is an other strange behaviour I realized:
When I wanted to set a text for the CustomerField.Text and ObjectAddress.Text property I got a NullReferenceException that the two objects CustomerField and ObjectAddressField are null which leads me to the conclusion that there must a wrong initialization of the ProjectViewController.
If you have some other recommendations about my code, feel free to tell my, because as I said I´m a rookie with Xamarin and App-Development.

Related

TableView scroll isn't smooth (Xamarin.iOS)

I have table view with images in cells
But when data is shown in TableView and I scroll it, it not smooth. But all images are downloaded I think.
Here is Code for TableView source
public class ExperienceSource : UITableViewSource
{
//UINavigationController instance, to pass it to constructor
private UINavigationController primNav { get; set; }
private UITableView tableView { get; set; }
List<Experience> TableItems;
ExperienceTableViewController owner;
public ExperienceSource(List<Experience> items,ExperienceTableViewController owner, UINavigationController nav)
{
TableItems = items;
this.owner = owner;
primNav = nav;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
if (TableItems.Count == 0)
{
var noDataLabel = new UILabel
{
Text = "No Experiences at your location at this time. Try to change destination",
TextColor = UIColor.Black,
TextAlignment = UITextAlignment.Center,
LineBreakMode = UILineBreakMode.WordWrap,
Lines = 0
};
tableview.BackgroundView = noDataLabel;
tableview.SeparatorStyle = UITableViewCellSeparatorStyle.None;
return TableItems.Count;
}
else
{
tableview.SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine;
return TableItems.Count;
}
}
public override async void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
var selectedExperience = await ExperienceMethods.GetSelectedTour(TableItems[indexPath.Row].id);
if (selectedExperience == "Saved")
{
ExperienceDetailViewController ExperienceDetailController = primNav.Storyboard.InstantiateViewController("ExperienceDetailViewController") as ExperienceDetailViewController;
primNav.PushViewController(ExperienceDetailController, true);
}
else
{
UIAlertController okAlertController = UIAlertController.Create("", "Cannot select this experience", UIAlertControllerStyle.Alert);
okAlertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
}
tableView.DeselectRow(indexPath, true);
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell("cell_id", indexPath) as ExperienceCell;
Experience item = TableItems[indexPath.Row];
cell.UpdateCell(item);
return cell;
}
And I have code for my custom cell in this class
public partial class ExperienceCell : UITableViewCell
{
public ExperienceCell (IntPtr handle) : base (handle)
{
}
internal void UpdateCell(Experience experience)
{
UIImage picture;
try
{
var image_url = "https://xplorpal.com/" + experience.cover_image.img_path + "/300x300/" + experience.cover_image.img_file;
using (var url = new NSUrl(image_url))
using (var data = NSData.FromUrl(url))
picture = UIImage.LoadFromData(data);
ExperienceImage.Image = picture;
}
catch { }
finally
{
ExperienceTitle.Text = experience.title;
ExperiencePrice.Text = "$" + experience.price;
}
}
}
Where can be my problem and how I can optimize scroll of this TableView?
Thank's for help.
I am not familiar with using NSData.FromUrl method inside a TableViewCell, but handling remote images manually is most of the time a bad idea.
You should consider using a library like SDWebImage, that will handle downloading, caching, and assigning the image to your UIImage for you.

Unable to dequeue a cell with identifier cell_id - must register a nib or a class for the identifier

I writing xamarin.ios app and using TableView.
I want to Create a custom cell.
So in the designer, I created it with this properties
I try to use it then in TableSource
Like this
public class ExperienceSource : UITableViewSource
{
Experience[] TableItems;
//NSString cellIdentifier = new NSString("TableCell");
ExperienceController owner;
public ExperienceSource(Experience[] items, ExperienceController owner)
{
TableItems = items;
this.owner = owner;
}
public override nint NumberOfSections(UITableView tableView)
{
return 1;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return TableItems.Length;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell("cell_id", indexPath) as ExperienceCell;
//Experience item = TableItems[indexPath.Row];
/*if (cell == null)
{
cell = new ExperienceCell(cellIdentifier);
}*/
cell.UpdateCell(TableItems[indexPath.Row].title, TableItems[indexPath.Row].price);
//---- if there are no cells to reuse, create a new one
/*if (cell == null)
{
cell = new ()(CellIdentifier);
}*/
return cell;
}
}
And Here is class for ExperienceCell
public partial class ExperienceCell : UITableViewCell
{
public ExperienceCell (IntPtr handle) : base (handle)
{
}
internal void UpdateCell(string title, string price)
{
ExperienceTitle.Text = title;
ExperincePrice.Text = price;
}
}
When I run app, I got this error
unable to dequeue a cell with identifier cell_id - must register a nib
or a class for the identifier or connect a prototype cell in a
storyboard
How I can fix this?
Thank's for help.
You are missing the registration of the UITableViewCell in the UITableView.
Please add something like this in your ViewController (the important part is the RegisterNibForCellReuse sentence):
public override void ViewDidLoad()
{
base.ViewDidLoad();
MyTableView.RegisterNibForCellReuse(UINib.FromName(nameof(ExperienceCell), NSBundle.MainBundle), "cell_id");
MyTableView.Source = new ExperienceSource(..., ...);
// .. your code
}
This is a necessary step when you declare your UITableViewCells outside the UITableView.

Dynamically navigating through n categories

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

Error on app launch: Foundation.MonoTouchException: Objective-C exception thrown (Xamarin iOS)

I get this error message when launching my app:
Foundation.MonoTouchException: Objective-C exception thrown. Name:
NSInvalidArgumentException Reason: -[TableSource initWithCoder:]:
unrecognized selector sent to instance 0x796e6fa0
I already searched on google, but didn't find a solution.
About the app: The App has a UITableView with some custom cells. The UITableView is on a normal "view". Theres also a Button on the normal "view", this button should (when touched) add a custom cell to the UITableView.
The UITableView has the name "tableView" and in properties at class "TableSource". The Button has the name "btn01" and in properties as class "ViewController".
The custom cells have as "reuse identifier" "Cell01Reuse", "Cell02Reuse", etc. And as class "Testclass" (which doesnt exist as a file).
The View Controller (the base, where everything is on) has "ViewController" as class.
I have two classes with code. First is "View Controller":
using System;
using UIKit;
using Foundation;
using System.Collections.Generic;
namespace myapp
{
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.
//UITableView _table;
//_table = new UITableView
//{
// Frame = new CoreGraphics.CGRect(0, View.Bounds.Height * 0.03, View.Bounds.Width, View.Bounds.Height * 0.80),
// Source = new TableSource(null)
//};
//_table.SeparatorStyle = UITableViewCellSeparatorStyle.None;
//View.AddSubview(_table);
TableSource TS = new TableSource();
btn01.TouchUpInside += (sender, e) =>
{
TS.updateTableView();
string cell01 = "Cell01Reuse";
TS.tableItems.Add(cell01);
};
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
}
}
Second is "TableSource":
using System;
using System.Collections.Generic;
using System.Text;
using Foundation;
using UIKit;
namespace myapp
{
public partial class TableSource : UITableViewSource
{
//string[] tableItems;
public List<string> tableItems = new List<string>();
public static string cellIdentifier = "TableCell";
//public TableSource(string[] items)
//{
// tableItems = items;
//}
public TableSource()
{
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return 0;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
UITableViewCell cell = tableView.DequeueReusableCell(cellIdentifier);
if (cell == null)
{
cell = new UITableViewCell(UITableViewCellStyle.Default, cellIdentifier);
}
cell.TextLabel.Text = tableItems[indexPath.Row];
tableItems.Add(Convert.ToString(cell));
return cell;
}
public override nint NumberOfSections(UITableView tableView)
{
return base.NumberOfSections(tableView);
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
new UIAlertView("Alert", "You touched: " + tableItems[indexPath.Row], null, "OK", null).Show();
tableView.DeselectRow(indexPath, true);
}
public void updateTableView()
{
tableView.updateTableView();
}
}
}
Actually I can not reproduce the issue you met, your description is not clear enough, but there are some problem in you code is for sure, such as you should not return 0 in RowsInSection method, I can give you a sample to reference.(All the UI is created by code)
public partial class ViewController : UIViewController
{
protected ViewController (IntPtr handle) : base (handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
CGRect tableFrame = this.View.Bounds;
tableFrame.Y = 100;
tableFrame.Height -= 100;
UITableView tableView = new UITableView (tableFrame);
this.View.AddSubview (tableView);
MyTalbeSource mySource = new MyTalbeSource ();
tableView.Source = mySource;
tableView.ReloadData ();
int count = 0;
UIButton btnNew = new UIButton (UIButtonType.System);
btnNew.Frame = new CGRect (20, 20, 100, 40);
btnNew.SetTitle ("NewItem", UIControlState.Normal);
btnNew.TouchUpInside += delegate {
mySource.AddNewItem ("NewItem" + count++);
tableView.ReloadData ();
};
this.Add (btnNew);
}
}
class MyTalbeSource : UITableViewSource
{
private const string CELL_ID = "MyTalbeCell";
private List<string> dataList;
public MyTalbeSource ()
{
dataList = new List<string> ();
for (int i = 0; i < 5; i++) {
dataList.Add ("Test " + i.ToString ());
}
}
public void AddNewItem (string title)
{
dataList.Add (title);
}
public override nint RowsInSection (UITableView tableview, nint section)
{
return dataList.Count;
}
public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
{
MyTableCell cell = tableView.DequeueReusableCell (CELL_ID) as MyTableCell;
if (null == cell) {
cell = new MyTableCell (UITableViewCellStyle.Default, CELL_ID);
cell.InitCell ();
}
cell.Text = dataList [indexPath.Row];
return cell;
}
}
class MyTableCell : UITableViewCell
{
private UILabel lbInfo;
public string Text {
get {
return lbInfo.Text;
}
set {
lbInfo.Text = value;
}
}
public MyTableCell (UITableViewCellStyle style, string cellID) : base (style, cellID)
{
}
public void InitCell ()
{
lbInfo = new UILabel ();
lbInfo.TextAlignment = UITextAlignment.Center;
this.AddSubview (lbInfo);
}
public override void LayoutSubviews ()
{
lbInfo.Frame = this.Bounds;
}
}
Hope it can help you.
Any question about Xamarin.iOS is welcome.

What is the best way to switch a UITableView from list to grouped?

I have a requirement where I initially have a list of messages only ordered by date/time. The requirement if for the user to be able to click on a UISegmentedControl (list of 4 buttons) and be able to change the UITableView from a straight list to a grouped list (ie. grouped by category of message).
From what I've read, once the style is set on a UITableView you can not change it. So what is the best approach to satisfy this requirement? Kill the view and re-create with the appropriate style?
Not that it makes a huge difference, I am using Xamarin Studio and C#, targeting Mono 3.2.1 and iOS 6+
Rather than killing the view and re-instantiating, just maintain references to two UITableViews, one of each of the appropriate types. Toggle between them using your Controller class. The following simple example puts the toggling button in the same UIView as the table, which is probably not appropriate, but otherwise shows the technique:
public class ChangeableSource : UITableViewSource
{
public bool Grouped { get; set; }
public override int NumberOfSections(UITableView tableView)
{
if(Grouped)
{
return 4;
}
else
{
return 1;
}
}
public override int RowsInSection(UITableView tableview, int section)
{
return 3;
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var cell = tableView.DequeueReusableCell("Default");
if(cell == null)
{
cell = new UITableViewCell(UITableViewCellStyle.Default, "Default");
}
cell.TextLabel.Text = String.Format("IndexPath {0} {1}", indexPath.Section, indexPath.Row);
return cell;
}
}
public class ToggleTableView : UIView
{
UITableView ungroupedView;
UITableView groupedView;
ChangeableSource changeableSource;
public void SetStyle(bool grouped)
{
changeableSource.Grouped = grouped;
if(changeableSource.Grouped)
{
ungroupedView.RemoveFromSuperview();
AddSubview(groupedView);
}
else
{
groupedView.RemoveFromSuperview();
AddSubview(ungroupedView);
}
}
public bool GetStyle()
{
return changeableSource.Grouped;
}
public ToggleTableView()
{
var btn = new UIButton(new RectangleF(10, 10, 150, 40));
btn.SetTitle("Change", UIControlState.Normal);
btn.TouchUpInside += (s,e) => ToggleStyle(this, new EventArgs());
var tvFrame = new RectangleF(0, 60, UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height - 60);
ungroupedView = new UITableView(tvFrame, UITableViewStyle.Plain);
groupedView = new UITableView(tvFrame, UITableViewStyle.Grouped);
AddSubview(btn);
AddSubview(ungroupedView);
changeableSource = new ChangeableSource();
changeableSource.Grouped = false;
ungroupedView.Source = changeableSource;
groupedView.Source = changeableSource;
}
public event EventHandler<EventArgs> ToggleStyle = delegate {};
}
public class TogglingTableController : UIViewController
{
public TogglingTableController() : base ()
{
}
public override void DidReceiveMemoryWarning()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning();
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
var view = new ToggleTableView();
view.ToggleStyle += (s,e) =>
{
view.SetStyle(! view.GetStyle());
};
this.View = view;
}
}
[Register ("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
UIWindow window;
TogglingTableController viewController;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
viewController = new TogglingTableController();
window.RootViewController = viewController;
window.MakeKeyAndVisible();
return true;
}
}
public class Application
{
static void Main(string[] args)
{
UIApplication.Main(args, null, "AppDelegate");
}
}

Categories