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
Related
Does anyone know why UITextView.layoutSubviews() is not called when rotating a device to portrait mode?
When rotating to landscape mode, these are called:
UIViewController.viewWillTransition
UIViewController.viewDidLayoutSubviews
UITextView.layoutSubviews
UILabel.layoutSubviews
But when rotating back to portrait, the UILabel.layoutSubviews() is called, but not the UITextView.layoutSubviews. This is in an empty project with no other code apart from traces in these methods.
layoutSubviews is usually called when setNeedsLayout() is invoked already in the previous invocation of run loop.
If the layout system does not think it needs to be called, it will not be called.
Ideally you should not override this function. You should just call setNeedsLayout() after making superview changes, and let the layout system call the default implementation of this function. Morever, you should define your subview layout needs inside auto-layout so it is able to get correct values from there.
If you want immediate update, you should call layoutIfNeeded().
This is because this is one of those methods that are called arbitrarily by UIKit framework and it may not be ideal for your layout needs.
There are 2 separate things here.
Understanding layoutSubviews(). I.e. when and where to use it.
How to achieve what you want to do the right way. I.e. doing something with the UITextView at device rotation.
About the layoutSubviews(), you should not put any logic here as your view is not having any sub views.
You may say that we expect iOS to call it, so we can put some implementation here, but again, that is not the right way. layoutSubviews() is not meant to alter the view itself, but just laying out sub views.
I would recommend reading more on layoutSubviews(). I learnt from here, when I started learning iOS.
Now to achieve what you want to do, i.e. do something at the time of device rotation, you proper way is to use viewWillTransition(to:with:) method of UIViewController.
In this method, you can put logic to do something just before the transition will happen.
And you can also put logic which will execute during the transition OR after the transition completes, using the UIViewControllerTransitionCoordinator parameter passed to viewWillTransition(to:with:)
Hope this helps!
I'm trying to give a widget far down in the widget tree absolute positions. Because it is somewhere down in the widget tree, it is most likely that there will be certain layout constraints set by its ancestors. I'm looking for something that can ignore those constraints and just use absolute coordinates.
If the explanation is confusing, I drew a little scheme of something that represents my wanted outcome:
I tried doing this using a Stack, but that makes it difficult to pass in absolute coordinates, even with a Positioned because setting the property left: 0.0 would use relative coordinates to the ancestor. So setting it to zero would not necessarily mean that the widget would be positioned at the top left of the screen.
I also tried using an Overlay but I the results are pretty much the same as with the Stack implementation.
What is the recommended way of doing this in Flutter?
I'll describe two ways to do this, but I won't provide the code or an example as I don't think putting this out there in a copy-paste-able way is necessarily a good idea - I agree with #Remi's comment that if you need to do this in an app, there's a good chance that the app could be refactored to avoid it. However, there is a way to do this - although be warned, it might cause some problems with things such as touch events (although I think there's ways to get around that - see AbsorbPointer and IgnorePointer for starting points). Since you haven't really explained what you want this for, I'm going to assume that isn't something you need.
I would encourage you to look into other ways of doing whatever you're trying to do and ask if you'd like help figuring out that better way.
Anyways, on to the good stuff =P :
Method # 1 - using an existing overlay:
I have actually used this method myself for a modal loading indicator which I want to persist while between pages underneath are being pushed and which can be called from anywhere within an app without requiring any widgets be in the tree above it. That's a very specific use-case though which I think justifies this usage.
Basically, you want to use an Overlay, but instead of creating your own (which will have the same size as the widget you're dealing with), you want to use an existing one. If you're using a MaterialApp, WidgetApp, or even just a Navigator somewhere in your app, you have an Overlay already which is almost for sure the same size as the screen. If you have multiple layers of overlays, you want to get the one at the top as that's the most likely to cover the entire screen.
To get access to it, you can use OverlayState rootOverlay = context.rootAncestorStateOfType(const TypeMatcher<OverlayState>()); to get ahold of the root overlay (you'll probably want to assert that it actually found one).
Once you have access to it, you can make an OverlayEntry which builds whatever your widget is, and call rootOverlay.insert(...) to add it. The item built by your OverlayEntry will be positioned from the top left and to the extent of the screen so long as the overlay itself covers the entire screen (you can do the offset yourself). You'll also want to make sure you rootOverlay.remove(...) at some point, which means keeping a reference to the OverlayEntry. I'd personally create a class called OverlayInsertionManager or something which keeps track of the overlay entry and does insertion/removal.
Method #2 - using your own widget
I would consider this way of doing it slightly cleaner if you're just doing it within your own app, although still probably not a great idea.
Basically, what you want to do is create a Stateful Widget high up in your app - above anything that would take up space on the screen. This could theoretically mean above your MaterialApp/WidgetApp/etc although that might cause problems for you if you're using theme/text directionality/etc that WidgetApp provides. I think you can use the WidgetApp.builder to place your widget where you need it. Let's call it PageOverlay for convenience (with a corresponding PageOverlayState.
In your widget you'll have a static of method like the following (these are littered throughout flutter's source code and are more or less a convention):
static PageOverlayState of(BuildContext context) {
final PageOverlayState result = context.ancestorStateOfType(const TypeMatcher<PageOverlayState>());
assert(() {
if (result == null) {
throw new FlutterError(
'No Overlay widget found.\n'
);
}
return true;
}());
return result;
}
Within your PageOverlayState, you're going to have a variable something like WidgetBuilder _overlayBuidler which is normally null, and a method/setter something like set overlayBuilder(WidgetBuilder overlayBuilder) => setState(() => _overlayBuilder = overlayBuilder);
In the PageOverlayState's builder, you'll create a Stack; the first child will be the child you get passed into the WidgetApp.builder or MaterialApp.builder. You'll only create a second child if _overlayBuilder is not null; if it is not null the second child should be something like new Builder(builder: _overlayBuilder).
You might have to do something around sizing the stack properly (i.e. put it in an Expanded or something).
Let's call this UIView subclass - SomeClass. This SomeClass is a part of a static library. Some customer will use this library and will add instances of this SomeClass to the cells of his (customer's) table view.
I (SomeClass) need to determine when the SomeClass "enters" screen (will become visible), and when will "exit" screen (will become non-visible).
I can use didMoveToWindow: method and then check self.window for nil. BUT, there is a problem, SomeClass gets this event, before it is actually visible, because of cells "preparation" by table view concept. And I need to know for sure, it is 100% visible by some user.
One way to determine is by using scrollViewDidScroll:. Suppose SomeClass will get scroll view by using iteration on super views and will subscribe as a delegate to found scroll view. But he will be removed by some cell that will subscribe itself as a delegate to scroll view. So I need to invent here some solution for this. For example, in Android, there is possibility to add observer, in that case SomeClass is always a listener and is not overriding any other listener. There is many to one relation in Android, not like in iOS, one to one.
The other way, I can enable some timer in didMoveToWindow: when SomeClass becomes visible, that will check each X time, its frame. The timer will be disabled, when SomeClass will go from screen.
Probably there is a way to check at low level, without using scroll view and timer on some low-level redraw method. Is it possible?
So what is the best (will use less resources / good design) method?
You can use CGRectIntersectsRect to check if the cell's frame intersects with the frame of your custom view. Aside from that, didMoveToWindow is the method you are looking for.
If as you say the table view cell will always have SomeClass as a subview, then it would make more sense to use UITableViewDelegate tableView:willDisplayCell:forRowAtIndexPath:.
Is there any possible way to detect every change on User Interface during runtime??
I'm trying to find all objects in the current app interface.
I'm trying to to get all nodes inspecting recursively the main Window, but, for example, how to know if the top viewcontroller changes or if it's added a uiview dynamically, or is presented a modalview??
The main objective is to have a library to do this..
Any idea, help?
Thanks!
You could write your own library based on this, using advanced Objective-C techniques. I do not recommend you to do this, since it mostly breaks MVC patterns on iOS. Depends on what do you want to use it for, maybe analytics?
So these are the options I believe, if you want to actively inspect UIView hierarchy. All options are pretty complicated though.
Swizzle methods such as addSubview and removeFromSuperview of UIView, so you could know when changes like that happens. Including the getters of frame and bounds, if you wish to know the position.
You could use KVO to watch properties such as: subviews, frame, bounds, superview to notice any changes. But at one point you would have to add the same object as the observer (could be singleton).
Decide for an interval that is fired by a NSTimer and go through the hierarchy recursively beginning at keyWindow on UIApplication. This would have a big performance impact though.
There may be other options, but these are the ones I believe to be the best choices.
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.