Split view controller memory management - cocoa

I use simple split view controller in my osx application... split item 0 is used for menu and second one is for content (like in slack application).
I get memory leak, so need optimizations ... here is what I do when menu item is clicked:
partial void SettingsClicked (NSObject sender)
{
HighLightMenuItem (SETTINGS_INDEX);
var svc = ParentViewController as NSSplitViewController;
SettingsVC = SettingsVC ?? Storyboard?.InstantiateControllerWithIdentifier ("settingsViewController") as SettingsViewController;
var svi = new NSSplitViewItem ();
svi.ViewController = SettingsVC;
DisableBack ();
svc.RemoveSplitViewItem (svc.SplitViewItems [1]);
svc.InsertSplitViewItem (svi, 1);
}

Related

Xamarin (Visual Studio), View mapped MKMapView, not a control on view, will not show navigation bars added to view

I created a View and mapped it to a MKMapView via code, but I cannot seem to have the Navigation Bar show up (it's the 2nd view in the stack, so it should have some ability to show the bar).
The map creates well with all functionality I want, BUT, it takes up the entire view space on the View
public override void LoadView()
{
CoreGraphics.CGRect r = new Rectangle(0, 40, (int)UIScreen.MainScreen.Bounds.Right, (int)UIScreen.MainScreen.Bounds.Bottom);
map = new MKMapView(r);
View = map;
}
any ideas?
To show Navigation Bar , you need make sure that the RootViewController of the Application is an UINavigationController (not an UIViewController) .
in Appdeledate.cs
public bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
Window = new UIWindow(UIScreen.MainScreen.Bounds);
Window.RootViewController = new UINavigationController(new YourViewController());
Window.MakeKeyAndVisible();
return true;
}
in SceneDelegate (if your app contains it)
public void WillConnect (UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
{
Window = new UIWindow(scene as UIWindowScene);
Window.RootViewController = new UINavigationController(new YourViewController()) ;
Window.MakeKeyAndVisible();
}
And when you navigate from the first page to the map page
NavigationController.PushViewController(new YourMapController(),true);

Ripple Effect gone after adding TapGestureRecognizer to ViewCell

I added a custom LongPressGestureRecognizer to the ViewCell's root layout to handle certain cases, but after adding it, I find that the ripple effect when tapping the ViewCell is gone on Android. I tried to add back the animation by getting the native view, set background drawable to Android.Resource.Attribute.SelectableItemBackground by using below code
int[] attrs = { Android.Resource.Attribute.SelectableItemBackground };
var ta = CrossCurrentActivity.Current.Activity.ObtainStyledAttributes(attrs);
var drawable = ta.GetDrawable(0);
nativeView.SetBackgroundDrawable(drawable);
ta.Recycle();
Even this doesn't work. Any other way to make it work?
For those who want to know, I discarded the custom long press gesture recognizer way of achieving the goal, since it's the wrong way of doing things. On Android, we should use ItemLongClick event instead. Here is what I did, first, find out the native ListView through some method, my way is to first get the renderer of the ListView, then get underlying ListView. Another way is to use below code to find the ListView, but this way requires more work if you have multiple ListView
public static List<T> FindViews<T>(this ViewGroup viewGroup) where T : View
{
var result = new List<T>();
var count = viewGroup.ChildCount;
for (int i = 0; i < count; i++)
{
var child = viewGroup.GetChildAt(i);
var item = child as T;
if (item != null)
{
result.Add(item);
}
else if (child is ViewGroup)
{
var innerResult = FindViews<T>(child as ViewGroup);
if (innerResult != null)
{
result.AddRange(innerResult);
}
}
}
return result;
}
var rootView =(ViewGroup)CurrentActivity.Window.DecorView.RootView
var nativeListView = rootView.FindView<Android.Widget.ListView>();
Then override the OnAppearing method of the Page, in it, attach ItemLongClick event handler. Also override OnDisappearing method, in it, detach the ItemLongClick event handler. This is important. Simply add ItemLongClick event handler in constructor seems not working.

How to hide a TabBarItem in Xamarin iOS

I have a Storyboard with a TabBarController and six subviews as Tabs. The TabBarController and each Tab has a Controller Class assigned.
The content of some tabs is loaded from a server. In some cases there is no content for a tab and for this case I want to hide the TabBarItem.
I've tried (just for testing)
this.TabBarController.TabBar.Items [0].Enabled = false;
but it doesn't work.
How can I hide a single TabBarItems? I've searched Google, StackOverflow and the Xamarin Forum but found no solution for my problem.
The only solution I've found was to remove the subview from the subviews-array, but in this case I cannot simply "reactivate" the tab if I want to show the tab again.
In case of UITabBar:
UITabBar sampleTabBar;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib.
sampleTabBar = new UITabBar ();
sampleTabBar.Frame = new CoreGraphics.CGRect (10f, 64f, this.View.Frame.Width - 10, 50f);
var tabBarItem = new UITabBarItem ("TAB BAR ITEM", null, 4);
sampleTabBar.Items = new UITabBarItem[]{ tabBarItem };
sampleTabBar.ItemSelected += (sender, e) => {
Console.WriteLine ("tab bar button item slected");
//Disable Tab Bar item here
tabBarItem.Enabled = false;
tabBarItem.Title = "Disabled now";
};
this.View.AddSubview (sampleTabBar);
}
tabBarItem.Image property also can be used for getting the result.
1) Set a placeholder image as default.
2) Populate images from array/source to uibarButtonItem.
3) Use placeholder image wherever need to hide uibarbuttonitem.
I think if you want to completely hide the UITabbarItem you will need to remove the UIViewController from the UITabbarController like so:
var tbViewControllers = new List<UIViewController> (TabBarController.ViewControllers);
tbViewControllers.RemoveAt (2); // remove whatever index you need.
TabBarController.ViewControllers = tbViewControllers.ToArray ();
but you will need to keep a reference to all the UIViewControllers you want and add it back in like so:
var tbViewControllers = new List<UIViewController> (TabBarController.ViewControllers);
tbViewControllers.Insert (2, new RemvoedViewController());
TabBarController.ViewControllers = tbViewControllers.ToArray ();

RecyclerView Click event

I have created a RecyclerView adapter and I'm trying to start an activity when a row is clicked:
public override OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyViewHolder viewHolder = (MyViewHolder)holder;
viewHolder.MyView.Click += (sender, e) =>
{
var context = viewHolder.MyView.Context;
var intent = new Intent(context, typeof(DetailActivity));
context.StartActivity(intent);
}
}
When I click the first row it will take me to the activity like I want. If I scroll down so that the first row is rebound and then scroll back to the top again and then click the first row then my Click event fires twice. Once for the first row that was bound and then again for a row that was bound when I scrolled.
Is there an event you need to handle to unregister the click events?
I believe the standard pattern is to setup your clickhandlers in the constructor of the ViewHolder. Then in OnBindViewHolder, you update the Views/Data inside the ViewHolder.
Something like this (not compiled code):
Adapter:
public override OnBindViewHolder()
{
MyViewHolder viewHolder = (MyViewHolder)holder;
viewHolder.SetData(whatever data you care about);
}
MyViewHolder:
public MyViewHolder(View view) : base(view)
{
MainView = view;
MainView.Click += (sender, e) =>
{
var context = MainView.Context;
var intent = new Intent(context, typeof(DetailActivity));
context.StartActivity(intent);
}
}
Doing it this way keeps the Adapter cleaner by putting business logic in the ViewHolder, and also prevents your click handlers from being constantly setup and torn down as you scroll.

Multiple Custom Cell's in a ListView (Cross Platform)

Currently with ListView's I've only found that you can create a template for cells, which makes each cell look exactly the same. You can't have multiple custom cells in the listview. There are work-arounds like hiding the content in the cell depending on the content, but this seems pretty hacky.
The reason I want to use a listview over a tableview is because we plan on doing inserts, deletions, dynamically showing certain cells, and listview's can be binded to a data source.
Create your own ViewCell which overrides binding context change method. When the binding changes set the ViewCell's view to one that matches the type of view model and also set the height of the cell. Below is a quick sample that should give you an idea how to accomplish it.
public class DataTemplateCell1 : ViewCell
{
protected override void OnBindingContextChanged()
{
var vm1 = this.BindingContext as ViewModel1;
if (vm1 != null)
{
this.View = new View1() { HeightRequest = 40 };
this.Height = this.View.HeightRequest;
return;
}
var vm2 = this.BindingContext as ViewModel2;
if (vm2 != null)
{
this.View = new View2() { HeightRequest = 80 };
this.Height = this.View.HeightRequest;
return;
}
base.OnBindingContextChanged();
}
}

Resources