I am overridng editActionsForRow method to create a custom delete button in iOS 11 when user swipes to delete the row. Deletion works but the row does not move all the way to left during the animation process. Note, I have specified delete row animation style to left. Despite that it still does not work.
In one of the forums it has been mentioned as a bug in iOS 11. Any thoughts on this.
Below is the code snippet
public override UITableViewRowAction[] EditActionsForRow(UITableView tableView, NSIndexPath indexPath) {
var actionHide = UITableViewRowAction.Create(UITableViewRowActionStyle.Normal, "Hide for now".Localize(), (arg1, arg2) => {
// delete the row from the table source and UITableView
GroupedChallenge[indexPath.Section].RemoveAt(indexPath.Row);
tableView.DeleteRows(rows, UITableViewRowAnimation.Left);
});
actionHide.BackgroundColor = UIColor.Purple;
return new UITableViewRowAction[] { actionHide };
}
Related
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!
I have a problem with my JavaFX project. There is a moment I can't understand. As far as I understand the following code should be able to handle all scrolling events of a table, which is an instance of TableView
table.setOnScroll(new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent scrollEvent) {
System.out.println("Hello!");
int i = 0;
int length = table.getItems().size();
for(Node n: table.lookupAll("TableRow")) {
if (n instanceof TableRow) {
TableRow row = (TableRow) n;
if(table.getItems().get(i).getType() == "fwfx") {
row.setStyle("-fx-background-color: forestgreen;");
}
i++;
}
if(i == length) {
break;
}
}
}
}
);
Whenever I launch the application it highlights row correctly only for visible rows. I found it out because
table.lookupAll("TableRow")
returns the set of only 17 nodes for me. although
table.getItems().size()
shows the correct number of rows. If I scroll down the table I see unapproipriate rows highlighted. I'm lost a bit.
So the question is how do I correctly handle the scroll events for my table? I need to process all rows of the table, not only visible.
So finally I found the way to handle scrolling events and I would like to share my experience. Using the Scenic View I found that TableView setOnScroll event is fired only when you scroll the mouse wheel with cursor over the column headers. But to be able to handle ScrollEvent when the cursor is above table data (what I needed in my example) one needs to use the EventFilter to be sure. For example, the colde below will handle all scrolling events of TableView's instance
table.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent scrollEvent) {
System.out.println("Scrolled.");
}
});
Scenic View also gave me a hint about what TableView consists of after
stage.show()
has worked.
Althougth I still have incorrectly highlighted rows)...
Listen to scroll bar value changes
for (Node node : dataTable.lookupAll(".scroll-bar"))
{
if (node instanceof ScrollBar && ((ScrollBar) node).getOrientation().equals(Orientation.VERTICAL))
{
((ScrollBar) node).valueProperty().addListener(formatBUIScrollChangeEventHandler);
}
}
This will be triggered in any form of scrolling on the table.
Have been spending hours then finally come out with this idea when solving my own situation.
The question is old, but you haven't posted/accepted an answer yet.
So, this schould help: tableView.refresh(). It's available in JavaFX since Java 8u60.
You have to call it inside your ScrollEvent. Also perhaps by sorting and dragging ScrollBar (see my answer here).
I have 2 Telerik's rad grids. First one is master and second one is detail. I can delete rows from both grids independently by pressing "Delete" button on toolbar above each grid. I also have "Refresh" buttons in toolbar of both grids.
The problem is with detail grid. When I delete item(s) the grid doesn't refresh. Calling Rebind method doesn't help. The only thing that helps is to press "Refresh" button in toolbar of master grid and select the row in master grid by mouse that was previously selected. After that I can see refreshed detail grid.
So, I don't need to press "Refresh" button in toolbar of master grid and select the row in master grid by mouse. I can refresh the master grid programmatically and only want to reselect the item that was originally selected also programmatically. I've tried this:
item.Selected = true;
But, it only visually selects the item in master grid and doesn't refresh the detail grid.
So, how to select the item in master grid programmatically in order to get the same effect as selecting it by mouse?
Thank you in advance.
I've just realised that your probably using different DataSource for both grids, but pointing to the same database, right? My example below uses the same datasource for both grids. However I made on a detail view versus a normal view by making some columns not visible. Maybe this strategy could fix your issue?
My first thought was to try implement the SelectionChanged event, or if not that, the SelectionChanging event. Put a refresh in there you see. But I didn't end up doing it that way.
I wrote a small program as below. It saves the edits to disk with any row change as long as its not a remove (I had trouble saving remove edits when the button was clicked it gave a null pointer exception on the remove command). It also saves changes just before closing the program (so that any delete rows are also saved then). I did find that the deleteOne and deleteTwo buttons (that delete from the first or second grid, respectively) do in fact cause the deletion to occur in both grids. So a possibility is you could use the radGridView1.Rows.Remove(row) or RemoveAt(i) command if that works in your situation?
Another possibility is that if refresh isn't working you could set the DataSource to null and then set it to the data source again, after deleting the row. This is a bit drastic but if it's the only thing that works? I'm talking about the data source for both grids.
My code is below:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Telerik.WinControls;
using Telerik.WinControls.Data;
using Telerik.WinControls.UI;
namespace RadControlsWinFormsApp1
{
public partial class RadForm1 : Telerik.WinControls.UI.RadForm
{
public RadForm1()
{
InitializeComponent();
}
private void RadForm1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'testdbDataSet.Customers' table. You can move, or remove it, as needed.
this.customersTableAdapter.Fill(this.testdbDataSet.Customers);
radGridView1.Columns["Address"].IsVisible = false;
}
private void radGridView1_RowsChanged(object sender, Telerik.WinControls.UI.GridViewCollectionChangedEventArgs e)
{
// if removing don't update, because if my delete button is pressed this
// will otherwise cause all sorts of problems and freezes the grid
if (e.Action != NotifyCollectionChangedAction.Remove)
{
try
{
customersTableAdapter.Update(testdbDataSet);
}
catch (DBConcurrencyException ex)
{
// unable to save right now, don't worry about it
}
}
radGridView2.Refresh();
}
private void butDeleteOne_Click(object sender, EventArgs e)
{
bool haveRemoved = false;
for (int i = 0; i < radGridView1.Rows.Count && !haveRemoved; ++i)
{
GridViewRowInfo row = radGridView1.Rows[i];
if (row.IsSelected)
{
haveRemoved = true;
radGridView1.Rows.RemoveAt(i);
}
}
}
private void butDeleteTwo_Click(object sender, EventArgs e)
{
bool haveRemoved = false;
for (int i = 0; i < radGridView2.Rows.Count && !haveRemoved; ++i)
{
GridViewRowInfo row = radGridView2.Rows[i];
if (row.IsSelected)
{
haveRemoved = true;
radGridView2.Rows.RemoveAt(i);
}
}
}
private void radGridView2_RowsChanged(object sender, GridViewCollectionChangedEventArgs e)
{
// if removing don't update, because if my delete button is pressed this
// will otherwise cause all sorts of problems and freezes the grid
if (e.Action != NotifyCollectionChangedAction.Remove)
{
try
{
customersTableAdapter.Update(testdbDataSet);
}
catch (DBConcurrencyException ex)
{
// unable to save right now, don't worry about it
}
}
radGridView1.Refresh();
}
private void RadForm1_FormClosing(object sender, FormClosingEventArgs e)
{
// ensure all data is saved back into database on close
customersTableAdapter.Update(testdbDataSet);
}
//private void radGridView1_CellEndEdit(object sender, Telerik.WinControls.UI.GridViewCellEventArgs e)
//{
//}
}
}
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.
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.