Xamarin IOS - Tableview not refreshing on PresentViewController when it is a TabViewController - xamarin

I have a TableView that is in a descendent controller of a TabViewController. The TableView has items coming from an API. There is also a create new TableItem form/controller which once it has been submitted I need it to present the ViewController with the table view, with an updated list of items from the API.
var storyBoard = this.Storyboard;
var viewController = (TabBarController)storyBoard.InstantiateViewController("TabBarController");
var tabs = viewController.ChildViewControllers;
viewController.SelectedViewController = tabs[1];
PresentViewController(viewController, true, null);
This works as expected in terms of presenting the correct view. But until I force a reload like so:
async Task RefreshAsync()
{
if (_useRefreshControl)
_refreshControl.BeginRefreshing();
if (_useRefreshControl)
_refreshControl.EndRefreshing();
BookingsTableView.ReloadData();
}
The view just displays the same items in the TableView when on the load of the page it should be querying the API and loading the list:
_bookings = await _apiService.GetBookingsForCustomer(model.CustomerEmail);
_dataSource = new BookingTableSource(_bookings, this);
BookingsTableView.Source = _dataSource;
BookingsTableView.ReloadData();
Should also note this app is only a simple proof of concept.. But how can I get it to Instantiate my TableViewController whilst still Presenting the TabBarController?
Thanks,
Danny

Solved this, just dismissed the ViewController with the booking form and called my refresh method in the ViewDidAppear override in the ViewController with the bookings TableView:
public override void ViewDidAppear(bool animated)
{
RefreshData();
}

Related

Xcode - How can i programmatically embed/change view controller within a container view?

I am trying to programmatically set / or change the embedded View Controller inside a Controller View.
I have 3 different View Controllers, that I would like to show in the Container View, all depending on if the user is logged in or not.
I have looked around and tried a bunch of code, I found one that worked, but the code changed the self view, and not the view containers view.
A lot of the code I have tried have also not been in Swift 3, so as a new app developer, this has been quite stressful, as I tried to convert it to Swift 3.
Can anyone provide a solution for changing the embedded view controller inside a view container? Thanks.
I might have found a solution for this. I'm answering here, in case it can help anyone else in my situation.
What I did was add a new View Controller and then embed it to the View Container - This will work as a "master view" - I then use this blank view controller to decide which other view controller should be changed within the self of the blank.
Here's some code I have in the blank view controller, but I suppose the blank view controller can also be used as a master view controller (in my case for "Account"), and then it can add the login/register view as a child.
override func viewDidLoad() {
super.viewDidLoad()
updateView()
}
private lazy var loginViewController: loginViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "loginViewController") as! loginViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
private lazy var registerViewController: registerViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "registerViewController") as! registerViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
private func add(asChildViewController viewController: UIViewController) {
// Add Child View Controller
addChildViewController(viewController)
// Add Child View as Subview
view.addSubview(viewController.view)
// Configure Child View
viewController.view.frame = view.bounds
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Notify Child View Controller
viewController.didMove(toParentViewController: self)
}
private func remove(asChildViewController viewController: UIViewController) {
// Notify Child View Controller
viewController.willMove(toParentViewController: nil)
// Remove Child View From Superview
viewController.view.removeFromSuperview()
// Notify Child View Controller
viewController.removeFromParentViewController()
}
private func updateView() {
add(asChildViewController: registerViewController)
/*
if segmentedControl.selectedSegmentIndex == 0 {
remove(asChildViewController: sessionsViewController)
add(asChildViewController: summaryViewController)
} else {
remove(asChildViewController: summaryViewController)
add(asChildViewController: sessionsViewController)
}
*/
}
Credit to this guy: https://cocoacasts.com/managing-view-controllers-with-container-view-controllers/
The easiest way to do that is to have 3 container views in Storyboard above each other, each one of them is connected to a different view controller, then in your code, make 2 of these 3 container views hidden and leave one visible based on your business needs.
This is described in detail in this blog post. Hope this helps!

Event handler ordering in Xamarin

I have a list view that I am popping up in Xamarin forms, that I want to hide if someone taps outside of the box. I have a tap gesture recognizer on the parent layout for the list view that handles that. In Android, it all works good. If I click off, it closes, but if I click on an element in the list view, it properly selects it. In iOS, the opposite happens. The gesture handler on the layout fires first and closes the list view without properly selecting the item.
So my question, is there a way to change the order on how the events are fired? If not, is there a better alternative to how I'm trying to accomplish this? Thanks!
If you are using ListView.ItemSelected or ListView.ItemTapped then I ran into the exact same issue the other day. The fix for me was to not use either of those and instead attach a TapGestureRecognizer to the ViewCell that is within the ListView. I also added an IsSelected property to the object that the ViewCell is being bound to so that I could change the background color of the item once it has been clicked.
public class SomePage : ContentPage {
private SomeModel _selectedModel; //It would be best to put this into your ViewModel
...
public SomePage() {
ListView list = new ListView {
ItemTemplate = new DataTemplate(() => {
ViewCell cell = new ViewCell {
View = new ContentView()
};
cell.View.GestureRecognizers.Add(new TapGestureRecognizer {
Command = new Command(() => {
if(_selectedModel != null) { _selectedModel.IsSelected = false; }
SomeModel model = (SomeModel)cell.BindingContext;
model.IsSelected = true;
_selectedModel = model;
})
}
return cell;
}
}
}
}

How to override the functionality of Back button when using Navigation Controller in Xamarin.ios?

I'm working on Xamarin.iOS.When i move from one view controller to another a navigation bar is added to the view on which i just moved and a back button appears. On clicking the back button it returns me to the parent view. But i want some different functionality rather than returning to the parent view.
Can anyone help me out!
This can be accomplished by creating a custom button, and then setting that button as the back button for the view controller, such as:
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
UIBarButtonItem backButton = new UIBarButtonItem("title", UIBarButtonItemStyle.Bordered, handleBack);
this.NavigationItem.LeftBarButtonItem = backButton;
}
public void handleBack(object sender, EventArgs e)
{
Console.WriteLine("back!");
}
I hope this helps!

Xcode storyboard compilation issue for version 6.3

I am working on a simple table with custom cell in a View controller (iPad App). I have created a separate popover view controller to add new record to the table (data entry only) and I am trying to use same view controller to edit the row also (the selected row in the table view). The popover view works well for creating the new record.
But when I created a connection from the table view cell to the same view controller, the compilation fails with following message:
Interface Builder Storyboard Compilation Error : Couldn't compile
connection: IBCocoaTouchOutletConnection: => anchorView =>
IBUITableViewCell: 0x7fad4ca76d70
If I make the connection as PUSH or MODAL, the compilation goes through and I can execute the app.
I wanted to have the Add/Edit record view as a popover as this has only 4 fields. Now it works only if it is push mode. Can you please help me in solving this issue?
I was able to get this resolved using the custom segue from the cell to the view controller (popover mode). Following are the steps:
Created the custom segue in Storyboard from custom cell to target view controller.
Created a new class file subclass of UIStoryboardSegue and linked this to the custom segue in storyboard.
Used the segue id in prepareForSegue (as usual).
Below is the code I used in the custom segue class:
class myCellSegue: UIStoryboardSegue {
override func perform() {
var sourceVC: UIViewController = self.sourceViewController as! UIViewController
var destVC: UIViewController = self.destinationViewController as! UIViewController
destVC.modalPresentationStyle = .Popover
destVC.preferredContentSize = CGSizeMake(600, 500)
let popoverPresentationVC = destVC.popoverPresentationController
popoverPresentationVC?.sourceView = destVC.view
popoverPresentationVC?.permittedArrowDirections = .Down
// ==== View parameters ========
destVC.view.backgroundColor = UIColor.purpleColor()
sourceVC.view.addSubview(destVC.view)
sourceVC.presentViewController(destVC, animated: false, completion: nil)
}
}
BTW - we can use one custom segue for multiple segue connections.

Split View Controller: How to connect Master View Controller to Detail View Controller?

(Xcode6-beta3, Swift, iOS8, iPad)
In an iPad split-view controller, how do I link the Master View Controller to the Detail View Controller?
In other words, when the user taps on an item on the left, how do I change the view on the right?
I know that in didSelectRowAtIndexPath, I need to call a method... but how do I call a method in the Detail View Controller from the Master View Controller?
Example
Imagine an app to display information on different types of cheeses. We begin by dragging a split-view controller onto the storyboard. A table of items in the master view on the left is set up to read as follows.
Swiss
Cheddar
Brie
On the right, there is simply a Web View inside of the detail view controller, named cheeseViewController. Therein, HTML documents about the selected cheese will be displayed.
An IBOutlet is wired from the web view into cheeseViewController, and a method named 'changeCheese' is set up in the Detail View Controller delegate to swap out the document.
How can I make a tap on "Cheddar" change the information in the detail view?
EDIT: Do I have to modify my AppDelegate.swift file? Using a Master-Detail template, I tried the following, with no luck:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
splitViewController.delegate = navigationController.topViewController as Paragraph
return true
}
I hope I understood your problem correctly: You would like to show the detail information of a selected cheese in your Detailview.
When you create a new Master-Detail-View application in XCode 6 Beta 3, there will be a variable called "detailItem" in your DetailViewController.Swift file:
var detailItem: AnyObject? {
didSet{
self.configureView()
}
You set this detailItem in your MasterViewController.Swift file in the following function:
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?){
if segue.identifier == "yourSegueIdentifier" {
let indexPath = self.tableView.indexPathForSelectedRow()
let cheeese = yourCheeseArrayWithDetailInformation[indexPath.row]
(segue.destinationViewController as DetailViewController).detailItem = cheeese
}
}
(Assuming, that you have linked the views with a segue with the identifier: "yourSegueIdentifier" and an array of detailinfo called "yourCheeseArrayWithDetailInformation")
The above mentioned function "configureView" in the DetailView can now access your detailItem, which contains the contents of "cheeese"
I hope this helps you.
Why don't you just post a Notification from didSelectRowAtIndexPath in your Master and add an observer in your Detail View most likely inside your viewDidLoad. You also can handle the selector within the observer method with closure.
If you didn't create a master-detail app (so you have no detailItem), you might use this:
if let
mySplitViewController = splitViewController,
detailView = mySplitViewController.childViewControllers.last as? DetailViewController {
// do something with it
}

Resources