How Xamarin.Forms adjusts iOS/Android native elements (auto)height/width? - ios

This is a general question and specific one.
For general purpose - to understand how XF works and specifically - for using in my custom controls.
So, the thing is that I should put the elements (generated by XF for specific system (iOS or Android)) into some custom place within custom renderer.
In a nutshell, it's embedding XF controls into native interface, in more details I need to put the controls into custom alert/popup.
The issue is that the final size of the elements (views) can be different from initially generated XF ones.
So I need further adjustment of them (for instance, to fit parent view bounds (as again, the parent size is custom one and usually platform-dependent and screen-size-dependent)).
Let's say, I have:
<controls:AlertPanel
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
where AlertPanel is:
public class AlertPanel : Frame
inside of the custom renderer (of the AlertPanel for iOS):
// container is my native (manually created) view
container.AddSubview(this);
and this is the renderer class (inherited basically from UIView).
But if I do this right on IsVisible attribute setting to true (and the whole UI control block is dynamically created (or at least positioned)), the bounds setup via:
this.Frame = new CGRect(...);
... doesn't quite properly reflect what I expect.
It's much better when I apply that after (for instance) 10 ms delay, however, this is neither nice approach, nor having clear understanding of which way this all works.
In addition, I haven't found any constrains applied to the view generated by XF, so I am considering the bounds are strictly defined or calculated dynamically.
Any clues?

Related

iOS Interface Builder: How to make templates

I've recently started developing an iOS app, which I've never done before, so it's been going a bit slow, but I'm learning, so that's understandable.
I want to make a custom interface, so I've been making subclasses of the default view classes (like UIButton) so that I can define custom drawing. I've been told this is the best way to define custom interface elements that can be reusable. It definitely seems to be working that way. However, I haven't been able to make elements completely reusable by just using a subclass.
For example, in order to prevent a button's text from changing color when it is clicked, I have to manually go into the interface builder and set the button type to "Custom." After that, code that I enter into the subclass's constructor to change attributes seems to work. But I have to do this for every button I add, and in code the "buttonType" attribute is read only. Is there a way for me to define (just once) certain attributes for every instance of my button subclass that I add to the interface?
My goal is to be able to have a button subclass or template that defines all attribute values that I want my buttons to have, and every instance that I add automatically reflects those properties without me having to change anything. More so, I want to be able to modify that subclass/template and have those changes reflected in every existing instance. I have to imagine that this is possible in iOS. There is simply no way to build sophisticated interfaces without this capability.
Define a custom Button class (inherited from UIButton) in your project and in the init set the properties which you wanted to be set across.
In the interface builder go to the the class inspector and enter the button to be of the previously declared button.
buttonType needs to be set for all the button as this is defined at initialization time and exposed as read only property. If you want absolute reusability for your case, create a view, with an embedded button in code. when you create a button, create using the static method buttonWithType.
Wherever you need, drag and drop a UIView and set the view type to be the custom view.

How are NSTableCellViews supposed to be laid out?

I have a fairly basic MainWindow.xib with a source list-style sidebar. I created it by dragging the Source List template into the window, which already contains two NSTableCellViews: HeaderCell and DataCell.
The latter consists of an icon (using NSImageView) and a label (NSTextField). Instead, I want the label and another, smaller label underneath. In IB, this looks as follows:
If I focus on just DataCell, it highlights accordingly:
Thing is, actually running the program, it looks nothing like the template:
Notice how the two NSTextFields just get smashed together into one. My understanding was that view-based NSOutlineViews (and view-based NSTableViews, for that matter) are supposed to be designed as a template from within IB. Instead, the dimensions from the template seem to get mostly ignored.
Here's the code that sets the view's values from the data source:
public class TourSourceListDelegate : NSOutlineViewDelegate
{
public override bool IsGroupItem(NSOutlineView outlineView, MonoMac.Foundation.NSObject item)
{
return (item as TourSourceListDataSource.Item).IsHeader;
}
public override NSView GetView(NSOutlineView outlineView, NSTableColumn tableColumn, MonoMac.Foundation.NSObject item)
{
if (IsGroupItem(outlineView, item))
{
return outlineView.MakeView("HeaderCell", this);
}
else
{
var data = item as TourSourceListDataSource.Item;
var dataView = outlineView.MakeView("DataCell", this);
(dataView.Subviews[0] as NSTextField).StringValue = data.Name;
(dataView.Subviews[1] as NSTextField).StringValue = data.Date_start.ToShortDateString();
return dataView;
}
}
}
I've tried overriding GetRowHeight, but that doesn't seem to resolve the problem (it makes more room, but still doesn't let the views distribute themselves properly), nor does it seem necessary.
I've also tried playing with the various Autosizing, Autoresizes Subviews, etc. toggles in IB, but that doesn't seem to produce intuitive results, and again, it doesn't seem necessary — the view as presented in IB is exactly what I want, just with slightly longer labels in practice.
I haven't tried converting this to AutoLayout yet.
What obvious step am I missing?
Some more info that probably doesn't make a difference: this is a Xamarin.Mac/MonoMac project with Xcode 5.0, MacOSX10.8.sdk, Xamarin Studio 4.0.12, Xamarin.Mac 4.0.12, and Mono 3.2.3 (targeting Mono / .NET 4.0). I've also enabled App Sandboxing.
What's important in interface builder is the view hierarchy. What kind of view is that cell? Are those labels really subviews of the cellview or not? The hierarchy should look something like:
One thing that's fishy that I see is accessing dataView.Subviews[0] and [1]. If you're adding subviews to your cells then should be creating your own NSTableViewCell subclasses, with each view connecting to the subclass' IBOutlet properties. The subclass doesn't need any code in its implementation, just the declaration of its properties in #interface, such as titleField and descriptionField, and an empty #implementation that auto-synthesizes them.
Then makeViewWithIdentifier (or apprently the glue MakeView in Xamarin) when passed the right identifier should create your NSTableViewCell subclass, and at runtime you can verify that using po dataView in the debugger. Then you access the subviews using the properties of your NSTableViewCell subclass' interface instead of assuming which view is in which position with the subview array, using dataView.titleField and dataView.descriptionField.
If your cell view has one text field then you can use NSTableViewCell without subclassing, but do connect up the textField outlet (its connected by default as long as you don't delete & recreate the cell view's label view) so you can access it through the property, again instead of having to dive into the subviews array.
All that said, it's not really clear why you're seeing what you are. It looks like those aren't the subviews you expect, and might even look like the wrong fonts as well as in the wrong positions. Using a custom subclass of NSTableViewCell and verifying its class at runtime is a good way of making sure it's creating the view you expect, but you can also dump the subview within the debugger using po [dataView _subtreeDescription].

MVC Model - Should controller access view's controls directly?

I'm learning iOS development stuff and what I have found in tutorials and books is that controller layer usually has access to the View's controls directly (textfields, labels etc.). Let's consider such an example:
Assume, that View has a label called lblResult and a textfield called txtDataToAnalyze. Than in controler interface we've got something like this:
#property (nonatomic, retain) IBOutlet UILabel* lblResult;
#property (nonatomic, retain) IBOutlet UITextField* txtDataToAnalyze;
and some #synthesize statements in the implementation file.
I have some experience with JavaSwing development, where most of thinks I'm writing manually without any GUI Builders, and what I usually do in MVC is to access the View's controls via getters/setter. For example: void setResult(String resString); or String getDataToAnalyze();. In that way, controller knows only what pieces of information are displayed in the view, and not how are they displayed. I think it is more flexible (it is easier to change the view layer later).
I know that iOS has some specific rules, has introduced XIB/NIB files etc so maybe my doubts are completely useless in case of iPhone/iPad development. But I am going to write some more serious application for iOS (actually "rewrite" it from Java Swing) and that's why I would like to ask you:
Do you think, I should change the way I am thinking and get accustomed to that new (for me) approach (xib files, creating GUI using drag&drop and providing controler with information about how data should be displayed in view) ?? Did you have similar doubts when starting with iOS?
Short answer:
Yes, I think you should definitely spend a little time getting accustomed to working with Interface Builder (IB) to make NIBs and storyboards and let IB create the IBOutlet and IBAction references for you for those controls with which you need to interact. Once you're proficient at it, you'll be impressed by your productivity in generating easily maintained code. Don't dismiss IB too quickly.
In terms of letting the controller interact directly with the IBOutlet and IBAction references, this is common practice for simple user interfaces. If you have some real world examples, post a new question with a screen snapshot and we can offer more practical guidance.
Long answer:
Part of your question seems to be driven by the apprehension in seeing view controllers that are doing detailed interaction with a view's controls. The thing is, if you want to isolate your controller from some of the implementation details of the view, then go ahead and subclass the view and put the view specific stuff in there. IB can interface with both view controller subclasses as well as view subclasses. So you can happily use IB and still isolate your view controller from some of these implementation details.
Personally, I only do this subclassing of UIView when the view hits some subjective complexity threshold (e.g. for me, that threshold is when I find myself doing some complicated animation, such as using CADisplayLink; complicated gesture recognizers, etc.). I also subclass those subviews that are logical entities of their own (e.g. UITableViewCell or UICollectionViewCell). But for simple views where I'm interacting with my model to setting a control's properties, interacting with text fields, etc., I think putting that in the view controller is fine. Having said that, if I have a lot of view-specific code in my controller which has nothing to do with the integration of my model with my view, then start subclassing the UIView and shifting the view-only code into that.
Implicit in your question is the notion of programmatically building view rather than using NIBs/storyboards. In my opinion, using Interface Builder (IB) to build your UI is much easier to develop and maintain. There might be some pedagogical value to doing a test project where you build your views programmatically, so you really understand what's going on, but after that, I think you'll find yourself quickly gravitating to storyboards. And you'll get plenty of chances to write your own non-IB code when you start doing things beyond the capabilities of the standard IB controls (e.g. complicated custom container views, etc.). There are definitely those who prefer to develop views programmatically, but I don't think you can beat the development speed and ease of maintenance of IB generated UIs.
I general, the controller does not know about the view, but the view knows about the controller.
The gang of four book says:
"MVC also lets you change the way a view responds to user input without changing its visual presentation. You might want to change the way it responds to the keyboard, for example, or have it use a pop-up menu instead of command keys. MVC encapsulates the response mechanism in a Controller object. There is a class hierarchy of controllers, making it easy to create a new controller as a variation on an existing one.
A view uses an instance of a Controller subclass to implement a particular response strategy; to implement a different strategy, simply replace the instance with a different kind of controller. It's even possible to change a view's controller at run-time to let the view change the way it responds to user input. For example, a view can be disabled so that it doesn't accept input simply by giving it a controller that ignores input events.
The View-Controller relationship is an example of the Strategy (315) design pattern. A Strategy is an object that represents an algorithm. It's useful when you want to replace the algorithm either statically or dynamically, when you have a lot of variants of the algorithm, or when the algorithm has complex data structures that you want to encapsulate."

iOS: create a XIB from a UIView

I have lots of Views in old projects that I have layed out in code, using absolute positioning. That code is really hard to maintain. I'd like to port the code to using interface builder, but I'd have to layout every view again by hand.
Is it possible to programmatically create a XIB file from an existing UIView (which has been created programmatically)? I have searched the docs but can't find anything.
XIBs are essentially XML files, I guess that if you inspect a few you'd understand the format for different controls, but that would need a lot of manual reading, writing & testing to complete for all views. Probably a better plan would be to start slowly migrating to IB-made views.
Yes, it's possible; however, the thing is, you need to set all essential controls and properties of your xib. You won't be able to see its actual look and feel unless you have executed the project in your simulator or on an actual device (that means hard code everything).
You would use loadview in initializing your xib. Here's a bit of it:
- (void)loadview{
// set your view, screen size, and other properties of your xib
}

blackberry layout change height of field

I have a custom field I have created that loads images from a url. What I would like to do is have the field take up no space and then when the image is loaded resize itself to the size of the image. I have almost everything done but I can't work out a way to get the layout to be re done after the image is loaded. It works if I specify the size of the image beforehand. Calling invalidateLayout on the parent will not work as the screen is visible, but just calling invalidate does nothing. What steps to I have to go through to make a field resize?
It would also be preferable if I could call the method on the custom view rather than the parent but this is not essential.
This is for blackberry 4.5.0.
First of all, you will have to invalidate the parent manager, because it does need the new size of your custom field in order to redraw correctly the whole manager. (If there is other fields in the manager after your custom one, or even a scroll).
In the top of my head, here's two solutions you can try to implement :
When your custom field is done downloading the image, call the parent manager to invalidate all fields in it (you will have to hold a reference to the parent manager in your custom field)
-- or --
create the custom field object, without adding it to the manager. Start downloading you image, when it is done, call a parent specific method that will add the custom field to the manager (you will still have to hold a reference to the parent manager in your custom field) (You can use insert if you want to add it between two field already present on the manager). Like that you will not have to resize the field, but only add it to the manager when it is ready to be shown.
Answer to your comment :
Then you should use the synchronized scope :
synchronized(UiApplication.getUiApplication().getEventLock())) {
// UI Code here
}
Basically in this scope, you should only use an invalidate, do your size change somewhere else, before this call.

Resources