Understanding alloy structucture - titanium-mobile

I am developing app with titanium alloy. I have multiple xml file. Every xml file has got same view and every view id's and function is same. this approch is correct or must I assign different id and different named function for all object for prevent memory leak. I mean every xml's proxy is same or different on memory?
home.xml
<Alloy>
<Window id="home">
<View id="Container" onTouchend="fooFunction"> </View>
</Window>
</Alloy>
detail.xml
<Alloy>
<Window id="detail">
<View id="Container" onTouchend="fooFunction"> </View>
</Window>
</Alloy>
other.xml
<Alloy>
<Window id="other">
<View id="Container" onTouchend="fooFunction"> </View>
</Window>
</Alloy>
And how to clean object from memory when I close window for prevent memory leak?
Edited for window closing event for prevent memory leak;
$.detail.addEventListener("close", function() {
// this listerner creates when window open for paused app event
Ti.App.removeEventListener("app:RefreshJson", fncRefreshJson);
$.Container.removeAllChildren();
$.detail.removeAllChildren();
$.removeListener();
$.destroy();
// listview creates on the fly when new window opens
// then I am adding it into $.Container
listView = null;
$.detail = null;
});

From the docs: IDs should be unique per view but are not global, so multiple views can have components with the same ID.
Your approach is fine. A couple of notes
If the id is omitted from the top level component in the view, then alloy will adopt the file name as the identifier. So if you left off id="home", then in home.js, you would still reference the window object as... $.home since that is the file name.
camelCasing is the common format to use in Alloy, so View id="container" might be the way to go.
memory leaks specific to Alloy can occur with Data Binding, so bindings must be properly destroyed. Other than that, the same Memory Management tips should apply. The big one is to not use global event listeners. If needed, you can use Backbone Events instead.
Personally, I've found it easier to use the same identifier for all my windows, such as <Window id="win"> (and $.win in the controller) so when switching around between view controllers, I don't have to look up or remember what the name or file name is for this particular window.

My article on this topic is three-years old. But, in a quick scan I think it all still applies today. http://www.tidev.io/2014/03/27/memory-management/
And how to clean object from memory when I close window for prevent memory leak?
It depends:
If the window is part of a tab group, then it will remain in memory as long as the app is running.
If it's a window that is opened in a navigation group or in a stack of windows on Android, it would depend on how you instantiated the window.
// if you do this:
var win = Alloy.createController('detail').getView();
win.open();
// then to clean up, after
win.close()
// you need to
win = undefined;
// which is why it's better to do this if you can
Alloy.createController('detail').getView().open();
// then, inside of detail, you'd call its Window.close()
// method which would close the window and remove the
// last reference in memory and the object would be GC'd
As an aside, if you are really creating multiple windows with code as similar as you show above, perhaps you should create a Widget. You'd pass in defining characteristics (options, names, child Views, etc.) when instantiating the widget. This technique won't necessarily help with memory management or performance. But, it will help eliminate duplicate code.

Related

MAUI CarouselView: how to imitate swipe effect in code? Swipe animation does not happen

.NET Maui CarouselView. In certain situations I want my app to take the user to the next card automatically. If I update CarouselView.Position or CarouselView.CurrentItem in code behind, it "jumps" to the next card immediately, no animation. Is it possible to imitate user's swipe? Or as a workaround, maybe somehow apply non-native-CarouselView animation manually to the CarouselView. Please advise.
The CarouselView contains a ScrollTo method that will animate the scroll for you. You either scroll to an index or a specific item.
Give your CarouselView a name in the XAML, and then in the code behind call the ScrollTo.
To scroll to an index:
carouselView.ScrollTo(6);
To scroll to a specific item:
var viewModel = BindingContext as MyViewModel;
var item = viewModel.Items.FirstOrDefault(m => m.Name == "TheBest");
carouselView.ScrollTo(item);
These methods have to be called from the code behind, so if you're using a MVVM approach, you'll need to fire an event or command from your VM for your code behind to act on.
For additional info, take a look at the ScrollTo method docs from Microsoft.

Interchange Children between two Layout<View>

I am developing in Xamarin for iOS, Android and UWP. I created a custom control called DragAndSwapContainer which implements two custom behaviors: DragBehavior and DropBehavior.
DragBehavior basically adds a PanGestureRecognizer to the View and emits an event when the View is being 'dragged' (and also translates the view according to the Pan done by the user). On the other side, DropBehavior listens to this events and emits other events when a View is passing over the container or when a View is dropped inside.
DragAndSwapContainer acts as a container that can receive other views, and as a draggable and droppable view at the same time. The goal of this container is to enable the user to swap between two views by dragging and dropping one into the other one.
When a View is dropped inside this Container what I do is, basically, to swap the Children Views that both containers have (the one with the view that was dragged, and the one receiving the dropped view).
Here's a piece of code for the DragAndSwapContainer (which inherits from Grid):
private void OnViewDroppedInside(object sender, DroppedEventArgs e)
{
var draggedViewContainer = e.DroppedView.Parent as DragAndSwapContainer;
draggedViewContainer.Children.Add(this.Children.First());
this.Children.Add(e.DroppedView);
}
Since each View can have only one parent, this works fine: Child View is exchanged by the DragAndSwapContainers.
Everything works fine as respects to iOS and Windows, but I am facing some problems with Android.
I am getting the following error https://github.com/xamarin/Xamarin.Forms/pull/8888 when swapping two views. After some debugging I conclude that the problem arises because while swapping children (in between the two lines of code of Children.Add) Android native renderers are disposed, so I lose track of the View.
Also it is something that doesn't happen always, seems that there's some timings issues. If I attach my Analytics service (which reports to a cloud server and adds some overhead) the issue happens way more often.
The error mentioned seems to be fixed in the Xamarin.Forms version I have (since it was merged already in v4.4.0) but I'm still having the exact same error call stacks.
Given this situation, is there another better way to exchange children between two Layout?

How is an .xib file translated to a UI element?

From what I understand, MainMenu.xib contains XML that is used to create the main menu for an application. My question is, how is this file loaded, read, and used to create the main menu? Can anyone help me to understand the inner workings of the code that translates MainMenu.xib into the actual main menu seen in the UI?
Here is my understanding of the process so far.
MainMenu.xib is compiled into an .nib
The .nib is loaded into a UINib object.
From here, the UINib object is somehow used to create the Main Menu.
This is the part I am confused about. How exactly is the UINib object used to create the Main Menu?
Imagine this the other way around: you have a live view hierarchy in memory, in your running application. Now you want to allow the program to be terminated while saving the state of the UI. How would you do that? You would somehow write all the important properties of the live views to disk; some kind of serialization and archiving process.
That's all a nib is: it's an archive of a bunch of objects that happen to be views. (It encodes the "base" state of the views, of course, before any user interaction.) A xib, as you correctly noted, is an XML rendering of that archive, which makes it cooperate better with version control.
If you take a look at the XML of a xib in Xcode, you'll see that it consists of entries like this:
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="161" id="KGk-i7-Jjw" customClass="LeakInfoStepsCell" customModule="LookoutEmailLeakAlert" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="170"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
...
Just a listing of various properties on the view and the values they should have when the nib is unarchived. So when you load a UINib, it's reading those properties and creating view objects, just as you might do manually in code, or as you would do for your own object via NSCoding.

Why might an NSTableView redraw every cell on scroll?

I have an NSTableView with 5 columns, each containing a stock NSTableCellView in the nib. (The stock cells have a text box and an optional image.) When populated, the table has around 50 rows. Everything displays fine, but scrolling performance is pretty bad. It looks like this is happening because every cell gets a drawRect: message for its full rect whenever the table scrolls. However, neither reloadData nor reloadDataForRowIndexes:ColumnIndexes: is getting called, so it's not that. It's not the contents of the cells, either: I tried commenting out all my code to just leave the default cell image and text for each cell, and performance is the same. While scrolling, none of the cells get updated. (I put a breakpoint in tableView:viewForTableColumn:row: to make sure.)
My implementation has the following delegate methods:
tableView:viewForTableColumn:row: in the delegate; this creates and populates new cells via makeViewWithIdentifier:owner:
numberOfRowsInTableView: in the data source; this returns a constant number
tableView:sortDescriptorsDidChange: in the data source
That's it! Not very complicated, and yet.
I feel like I'm missing something completely obvious. What could be causing these redraws?
EDIT: Come to think of it, several other applications (uTorrent, Xcode) seem to exhibit the same slow scrolling behavior. You can really see it if you look at CPU usage while scrolling. On the other hand, Activity Monitor has buttery-smooth scrolling that barely spikes the CPU at all. How do I get that in my app?
EDIT 2: I think I found my mistake. According to Apple:
In iOS apps, Core Animation is always enabled and every view is backed
by a layer. In OS X, apps must explicitly enable Core Animation
support by doing the following:
Link against the QuartzCore framework. (iOS apps must link against this framework only if they use Core Animation interfaces explicitly.)
Enable layer support for one or more of your NSView objects by doing one of the following:
In your nib files, use the View Effects inspector to enable layer support for your views. The inspector displays checkboxes for
the selected view and its subviews. It is recommended that you enable
layer support in the content view of your window whenever possible.
For views you create programmatically, call the view’s setWantsLayer: method and pass a value of YES to indicate that the
view should use layers.
Enabling layer support in one of the preceding ways creates a
layer-backed view. With a layer-backed view, the system takes
responsibility for creating the underlying layer object and for
keeping that layer updated. In OS X, it is also possible to create a
layer-hosting view, whereby your app actually creates and manages the
underlying layer object. (You cannot create layer-hosting views in
iOS.) For more information on how to create a layer-hosting view, see
“Layer Hosting Lets You Change the Layer Object in OS X.”
I'll add an answer as soon as I fix my performance issues. With a cursory pass, my scrolling is still bumpy, but my CPU usage has dropped from 70% to 10% while scrolling.
For the record... Edit 2 by the OP makes the world of difference.
In iOS apps, Core Animation is always enabled and every view is backed
by a layer. In OS X, apps must explicitly enable Core Animation
support by doing the following:
Link against the QuartzCore framework. (iOS apps must link against
this framework only if they use Core Animation interfaces explicitly.)
Enable layer support for one or more of your NSView objects by doing
one of the following:
In your nib files, use the View Effects inspector to enable layer
support for your views. The inspector displays checkboxes for the
selected view and its subviews. It is recommended that you enable
layer support in the content view of your window whenever possible.
For views you create programmatically, call the view’s setWantsLayer:
method and pass a value of YES to indicate that the view should use
layers. Enabling layer support in one of the preceding ways creates a
layer-backed view. With a layer-backed view, the system takes
responsibility for creating the underlying layer object and for
keeping that layer updated. In OS X, it is also possible to create a
layer-hosting view, whereby your app actually creates and manages the
underlying layer object. (You cannot create layer-hosting views in
iOS.) For more information on how to create a layer-hosting view, see
“Layer Hosting Lets You Change the Layer Object in OS X.”

Cocoa bindings only update when window focus changes

I am using MonoMac to build a desktop download manager for Mac in C#.
My XIB has a Table View, whose columns are bound to an NSArrayController. The array controller is connected to my Main Window Controller through an IBOutlet. The array holds a bunch of HttpDownload objects, which derive from NSObject. These HttpDownload objects contain properties such as TotalSize, TotalDownloaded, Bandwidth, etc. I have decorated these properties with an [Export] attribute.
In the controller I add some HttpDownload objects to the NSArrayController using the AddObject method. A background process, started with Task.Factory.StartNew() begins the download asynchronously and updates the bound properties such as TotalDownloaded and Bandwidth as data is received.
I can see these new values being reflected in the Table View, but only once I've "forced" a UI update, for instance by causing the window to lose focus, gain focus, or by clicking on a button within the window.
I have tried setting Continuously Updates Value in IB, but this makes no difference (and reading the docs, I didn't think it should).
Does anyone know to make the UI update the bound values in "real time", instead of only when a window event occurs?
I figured this out shortly after I posted this question.
It seems that we need to manually call WillChangeValue() and DidChangeValue() for at least one of the keys that are being updated, for instance, when I updated the total downloaded:
WillChangeValue("DownloadedBytes");
DownloadedBytes += bytesRead;
DidChangeValue("DownloadedBytes");
In my case, calling these methods for just one of the updated keys seems to be enough to force an update of all the bound values.
For reference, in Objective-C these selectors are called [self willChangeValueForKey:#"keyname"] and [self didChangeValueForKey:#"keyname"].

Resources