I want to find a way how I can compare the actual hierarchical order of two subviews.
As i didn't find a method who turns out to give me this kind of result, I want to ask you. Im sure there must be a way to do this.
Is it possible, that the [view subviews] array is ordered with the same hierarchy?
Thanks,
Makrus
Although I have not find any reference about that in docs I'm pretty sure that the order of elements in subviews array corresponds to their z-order. You can try to change order of subviews in IB and log subviews to console - you will see that output supports that.
One more hint that it is really so are insertSubview:atIndex: and exchangeSubviewAtIndex:withSubviewAtIndex: methods which change subview's z-order and those changes are reflected in subviews array order...
Related
I have question regarding about finding subview using '.tags' in one UIView.
for (UIView *viewObj in [self.view subviews])
{
// want to find viewObject by its tag.
// but I heard for-switch or for-case paradigm are bad pattern design by WTF.
// there are total 9 of tags will be use for finding view object
switch (viewObj.tag)
{
case 0:
.
..
...
}
}
How can I make this to be non-switch or non-if statement? Should I just add into one array then fetch from there? For example, you add views with desired tags and fetch from this array.
Thank you.
From your comments, it's a very finite set of items that will exist - 9 items. If that is expected to be static, there is nothing really wrong with a switch statement. Perhaps you can functionally decompose each switch into a separate method. That said, perhaps the command pattern would be one approach to consider, in addition to your array idea. For reference: http://en.wikipedia.org/wiki/Command_pattern
If it is only going to be a fixed number of views and each view has different methods that need to be performed you could access the views directly.
UIView* someView = [self.view viewWithTag:0];
//Operations on view 0
UIView* someOtherView = [self.view viewWithTag:1];
//Operations on view 1
//...
One thing you could do is have the UIViews in your subviews be of the same type by having each conform to the same protocol. Then you could cast each to id<YourProtocol> and call the same method within each UIView.
And to further Shan's answer, if you can decompose them into functions and put them (function pointers) into an array, then you may be able to make it a little simpler, or you can use the new "Blocks" language feature.
There's a new IBOutlet type, IBOutletCollection. You use that for an NSArray, and then in IB you can add views to that outlet - at runtime, they will all be put into the array (unordered).
To get out a specific one you want, in viewDidLoad you could map all of the NSArray entries into a dictionary keyed by tag value, then just use "objectForKey" to get them out.
How does look the practical usage of IBOutletCollection? Unfortunately Apple documentation mentions it briefly without giving an wider idea of usage. OK, it maintains one-to-many relation with IB, but how to access and use particular objects efficiently? With TagName? How to ensure the order of objects?
I've recently used this to easily initialize a grid of labels. I have a n by n matrix of labels on a view, and reference each one individually (via an IBOutlet) in order to display relevant data. However when the view first loads I wanted to control the default text that displayed in all the labels. Initially i wanted a dash to display, but since this is for a client I wanted it to be easy to change. The view contents have and continue to change over time, per client requests.
Instead of writing N line of code, I created an IBOutletCollection and accomplished the same results in 4 (#property, #synthesize, and for loop). YMMV but I found it very useful in this situation.
Read again this section in the Interface Builder User Guide.
IBOutletCollections are actually just NSArrays you can connect to more than one object in IB. All the objects you connected end up in this array and can be accessed from code like any other object in an array.
I used it to minimize code. I have a range of UIViews that should react on "touch up inside" events of some UIButtons (custom mode).
I gave all UIButtons a tag (lets say 1005 to 1010) and all UIViews the same tag as the UIButton they shall respond to.
Then I connected the UIViews with the collection in Interface Builder. All UIButton touch up events go to the same function in my controller. This function gets the tag of the sender object, iterates through the NSArray list (of "IBOutletCollection(UIView)") and compares the tag. Everytime it hits, the appropriate action is done.
It is a pity that NSArrays seem not to hold the order...
I want to populate a scrollView with quite a few different UI elements.
Therefore I thought I would write a method that remembers the current Position in the scrollView and just adds the element to the scrollView at the current Position.
Something like:
- (void)addUIElement:(id)element withWidth:(CGFloat)width andHeight:(CGFloat)height andYGap:(CGFloat)YGap {
element.frame = CGRectMake(currentScrollPos.x, (currentScrollPos.y + YGap), width, height);
[scrolly addSubview:element];
//And then set the current scroll position here
}
Unfortunately when I try to do access element.frame = ..., I get request for member in something not a structure or union. When I try to do [element frame] = ... Lvalue required as left operand of assignment.
Now, first of all I am not sure what's the best way to dynamically add objects to a scrollview. Maybe anyone has a better or easier approach.
Then on the other hand, I don't get why the above does not work?! Would I have to cast my element to the actual class? I thought I would not have to do so... Also then my method would not make that much sense anymore. Or at least would require some more steps...
This should work I think:
[element setFrame:...];
However if you work with different UI elements in your method may be you can make your elements parameter UIView* instead of id? This way your code will work for all UIView subclasses (which is what you actually need I suppose)
The difference is that "id" doesn't have any kind of reference to a frame. It could be anything. You want to instead do (UIView *)element in the method declaration, or alternatively in the call to element.frame, you would do ((UIView *)element).frame.
(And yeah, all things that you put on the screen are inheriting from UIView -- UIButton, UIImageView, etc.)
I have a NSMutableArray that holds a collection of UIViewControllers
Amongst other properties and methods an instance of each of the viewControllers are placed in a parent view
The user will place these objects individually when desired to.
So essentially I use - (void)addSubview:(UIView *)view when a new view is added to the parent view controller.
Because the UI is isometric it's made things a tad more complicated
What I am trying to do is re-order the views based on their co-ordinate position, so items higher up the parent UIView frame is indexed lower then views lower in the parent UIview frame. And items that are on the left side of the view are positioned at a higher index to those on the right
I think the solution may have to do with re-ordering the NSMutableArray but how can I compare the CGpoints? Do I need to compare the x and y separately?
So users can drag views around inside a parent view? And when a view changes position, you want to change its place index among the subviews so that the draw order will be correct?
You can use -(void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2 do do this. It won't be enough to just reorder them in you array, since the parent UIView has its own array to keep track of the views. You should create a reorder method where you switch the views around both in your own array and in the parent view.
There are functions for checking equality of points, inclusion of points in rects, intersection of rects, etc, but here it just sounds like you want to get the origin.y point and use that to compare which views are further "back" or to the front...
As an aside, you might want to look at using CALayers, which have support for setting a z position as well.
This might be a simplistic answer, but maybe it's what you're looking for.
Assuming you have access to their CGpoints, you could write a...
-(NSComparisonResult) compare: (NSObject *) incoming;
method in every delegate that looked at the CGpoints of the incoming and "self" and returned a NSComparisonResult. Cool thing about this would be that you could just run...
[myMutableArray sortUsingSelector:#selector(compare:)];
anytime anything changed. You just have to setup the compare: method to return the right NSComparisonResult.
I've managed to resolve this, I did some reading on Key-Value Coding
I created a property called itemY to store the view.frame.origin.y value.
I created a method to re-assign the itemY with the current view.frame.origin.y value.
Now the ViewController that stores the array of ViewControllers uses a instance NSSortDescriptor with the property itemY.
A new NSArray is created using the NSSortDescriptor
Now I loop through the new NSArray and carry out my logic
Say I have 4 UIViews, made in IB, all with the tag property = 2
When I get a view with:
UIView *thisView = (UIView*)[self.view viewWithTag:2];
What is the criterion for retrieving that UIView since several have the same .tag value?
Is it:
random
the first one created
the view with the lowest index in it's superview
something else
Its weird but the view that is added first will be returned if you try to get the views among the views with same tag. You can check it in this way too.
NSLog(#"%#",[[self.view viewWithTag:custTag] class]);
Hope this helps.
If you use Interface Builder, it depends which order you use. In my case, UIActivityIndicator will be my result, and not UIWebView or UIButton with the same tag.
if I had to guess, I would assume that it would be almost-random. as in, you'll probably get some amount of consistency, but every so often it'll be something completely different (heh).
I'd say use different tags for them?