blackberry layout change height of field - blackberry

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.

Related

Can we access the objects in Storyboard by ObjectID

I have a bunch of static objects (UILabel, buttons, views) in multiple Scenes. They are not connected to any IBOutlet. But I'd like to access them at appdelegate (or first VC), and change their properties before it is loaded.
Anyway to do this?
EDIT: Adding my intention:
I actually wanted to make a custom "multi-language" app. I want to be able to change language from within the app. I can get a list of all the objects by applying built in localization of storyboard (Main.strings is autogenerated). Then I disable localization again. Then from this autogenerated file, I want to be able to connect it to a json data based on language that I select.
Of course you can. For example, you can use tags of UIView. Just set tags in Storyboard. It's easy but not so good. Another way to do this is using Accessibilities. Enable and set for it in Storyboard.
And then you can access it by accessibilityIdentifier property.
I will post my choice of "solution". So what I did was make use of accessibilityIdentifier to set the "key" for the multilanguage phrase translation purpose.
And I make use of the UIView+Recursion class (you can find this simple class somewhere in SO), and basically iterate all the objects in a particular Scene and if the text matches, set the key in accessibilityIdentifier property (either in viewDidload or viewWillAppear or viewDidlayoutSubviews).
This way you can have language changes "on-the-fly" within the app, without restarting.

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 do I re-layout and re-paint a field when a Manager calls setPositionChild(Field)?

My class extends Manager and calls setPositionChild(Field f) to change f's position. After calling setPositionChild() method, how do I apply the position(i.e. re-layout and re-paint) so I can see the changes?
I tried to call invalidate(), which did not work.
invalidate() just forces a repaint, it doesn't redo the whole layout, as you noticed.
You can force a relayout by going up to the parent Screen object, and calling invalidateLayout(). Forcing the layout will almost certainly call setPositionChild() on the field you are trying to move, so you will want to make sure the new field position is preserved by the manager layout code. This may mean you need to write a custom manager.

What's the difference between Screen.invalidate() and Screen.invalidateLayout()

void invalidate()
Invalidates the entire screen.
void invalidateLayout()
Invalidates this screen's layout (including all controlled fields).
The javadoc doesn't help much. Could you tell me why do we need invalidateLayout()?
Depending on how you build your UI, you may be interested in invalidating, or updating layout, when you want the display to change.
If you have a UI that's composed of standard Field objects, like TextField, or ButtonField, or BrowserField, without much customization, then the work of defining how the UI looks is mostly in how those fields are laid out. Layout refers to setting the size of fields, and their position.
Sometimes, you'll have a UI where you need to deal at a lower level. For example, you may do some custom painting, which involves drawing lines, filling areas, drawing gradients, etc. This work is normally done in a Field object's paint() method. Sometimes, you also have drawing code in other places like drawFocus() or paintBackground().
If you want to trigger repainting, which calls your custom paint() method, then you want to use invalidate() to make that happen.
If you want to trigger a new layout, which arranges child fields with certain sizes, or positions, then invalidateLayout() can be used for that.
However, I will note that invalidateLayout() is a method in the Screen class. If you use a RIM Screen subclass, like MainScreen or FullScreen, then they come with their own top level delegate Manager objects. I believe that calling Screen.invalidateLayout() will trigger the sublayout() method on that built-in Manager to be called. That may not be what you really want.
Unless you implement your own Screen subclass like this:
public class MyScreen extends Screen {
public MyScreen() {
super(new MyCustomManager());
}
}
you probably aren't implementing the code (including sublayout()) for that delegate manager yourself.
I have found that when I want to force sublayout() to be called again, I trigger that with the updateLayout() method, not invalidateLayout(). Also, as opposed to invalidateLayout(), which is a method of the Screen, updateLayout is available for all Manager and Field classes, so you can choose just the specific part of the UI element tree that you want to refresh. But, that's just my experience.
Here's some comments on the BlackBerry forums on this topic

Binding a view's layout property to an Object controller

I've recently begun working in Sproutcore, which seems to be a really good solution for client side web app development. However, the documentation isn't as thorough or concise as I'd like, so I am struggling with a few things.
Specifically, I'm trying to bind the layout property of one of my views to an object. I've managed this to an extent, in that when the view is rendered it uses the properties from the object, but the problem I'm having is that when the object is updated, the view's dimensions don't change. However, if the change is persistent and I reload the page, it uses the new values.
Is there some sort of limitation in binding layout properties so that they dynamically update, or do I have the wrong approach here?
I'm not sure this approach will work. Controllers are not supposed to have anything to do with a view's properties; controllers are ONLY supposed to proxy objects.
One alternative you might want to consider is using the adjust method defined on the SC.View class. You can have your view observe a property on the model it represents, and then in the observer call
this.adjust('height', 30); // or whatever
I think adjust can also be used like
this.adjust({
height: 10,
width: 20,...
})
without knowing more about what you are trying to do, its hard to say more.
MORE DETAIL
Ok, so one thing about Sproutcore is you need to be careful to not get in the way of the runloop. Unfortunately, there isn't much documentation on this. What I have learned is that you wan't observers to observe things in in their scope only. So here is an outline of what you want to do.
SC.View.extend({
layout: {...}, // initial properties
// binding to the text field that adjusting depends on, NOT on
// this view's content
outsidePropertyBinding: "binding.to.textField",
outsidePropertyDidChange: function(){
var outsideProperty = this.get('outsideProperty');
this.adjust({
// as shown before
})
}.observes('outsideProperty') // <-- this is how you create an observer
});
What I've shown here is how to adjust based on something that is changing outside of this view. I created
1) A binding to the outside property, which SC updates for you
2) An observer on the bound property, that fires as soon as the value is set, and adjusts the view. The observer is observing a property in the view, not out of the view.
Note that if your view is bound to some content, and its a property on that content itself that changes, then you would do it slightly differently. You wouldn't need the binding to the outside property, you could instead just observe '*content.relevantproperty'. The * before content tells the observer that the content object itself might change (if the views content object can change).

Resources