UIView: How to find out if a view is already exists? - iphone

I was wondering how to find out if a subview (in my case pageShadowView) has already been added to my view.
I've come up with this, but it doesn't really work:
if ([pageShadowView isKindOfClass:[self.view class]]) {
[self.view addSubview:pageShadowView];
}
Also, I'm still confused about the self.-thing. I know that this has to do with making clear that we are talking about the view of the current ViewController ... but do I really need it if there (1) are no other ViewControllers or (2) if it doesn't really matter because if I ever wanted to refer to another viewController, I'd make sure to call it?
I'm sorry if this is all very basic, but I'd be very grateful for your comments.

Here:
BOOL doesContain = [self.view.subviews containsObject:pageShadowView];
And yes, you need this self. There is no explicit ivar "view" on UIViewController. The self.view statement is actually a call on method [self view] which is a getter for UIViewController's view.

Give it a unique tag: view.tag = UNIQUE_TAG, then check the container view for existence:
BOOL alreadyAdded = [containerView viewWithTag:UNIQUE_TAG] != nil;

you can find a sub view like this
for(UIView *view in self.view.subviews)
{
if([view isKindOfClass:[UIView class]])
{
//here do your work
}
}

There's one more way to find, in Swift: isDescendant(of view: UIView) -> Bool or in Obj-C: - (BOOL)isDescendantOfView:(UIView *)view
Swift:
if myView.isDescendant(of: self.view) {
//myView is subview of self.view, remove it.
myView.removeFromSuperview()
} else {
//myView is not subview of self.view, add it.
self.view.addSubview(myView)
}
Obj-C:
if([myView isDescendantOfView:self.view]) {
//myView is subview of self.view, remove it.
[myView removeFromSuperView];
} else {
//myView is not subview of self.view, add it.
[self.view addSubView:myView];
}

SWIFT VERSION:
let doesContain = self.view?.subviews.contains(pageShadowView)

best and easy way to find that your view is exist or not , there are many way like you check a view is contain or not in super view or just view some time this method is failed because if the Uiview is already remove then error occur,
so code is here :
here errorView is my UiView
errorView.tag = 333
if ( self.view?.viewWithTag(333) != nil ){
print("contain")
}
else {
print("not contain")
}

To add to what coneybeare said, you could do the following. If you set your object.tag=100;
if ([self.view.superview viewWithTag:100] == nil){ //if statement executes if the object with tag 100 in view.superview is absent (nil)
if ([self.view viewWithTag:100] == nil){ //if statement executes if the object with tag 100 in view (not superview) is absent (nil)

add a retain value of the view
then check the retain value
if > 1 , then exist , if perfect should be 2
then release it once

Related

On iOS, is there a way to search ONLY subviews with a certain tag?

Because right now, viewWithTag actually search for itself first, and then all subviews recursively down the whole subtree, for a view with that tag.
But what if I set the tags of the subviews to 100, 101, etc, and later on, look for tag 100, but the parent of this current view sets the current view's tag to 100? Then viewWithTag will return the current view instead of any subview.
It is also strange that if the code is
[fooView viewWithTag: 123]
why would the code want to search the subtree including fooView itself? It is like, the code doesn't know fooView good enough to want to search for it too. Or put it another way, fooView is told to search itself... which is strange. A view doesn't know itself? (need to do a search to look for itself?)
So is there a way to search for subviews and grand-subviews only (without searching for self)?
Take advantage of the recursive nature of -viewWithTag:
- (UIView *)viewWithTagNotCountingSelf:(NSInteger)tag
{
UIView *toReturn = nil;
for (UIView *subView in self.subviews) {
toReturn = [subView viewWithTag:tag];
if (toReturn) {
break;
}
}
return toReturn;
}
Edit: this will drill down farther than "grand-subviews": it will get any view within the hierarchy that is not self. Also this is to be implemented in a category on UIView.
let result = view.subviews.filter{$0.tag == tag}.first
After reviewing the documentation for -viewWithTag: and running a few tests, it appears the answer to OP's question is - This behavior is already provided.
Return Value
The view in the receiver’s hierarchy whose tag property matches the
value in the tag parameter.
Discussion
This method searches the current view and all of its subviews for the
specified view.
I am concluding this to mean that 'view' is also a 'subview', thus limiting the scope of the search.
do this:
NSMutableArray *arrSameViewTag = [NSMutableArray array];
for(UIView *subview in [yourView subviews]) //your view to find subview
{
if(subview.tag == 123) //specific tah here
{
[arrSameViewTag addObject:subview]; //view found add in array
}
}
NSlog(#"arrSameViewTag : %#",arrSameViewTag);
To find specific like UIButton or any UIElement then like this:
NSMutableArray *arrSameViewTag = [NSMutableArray array];
for(id *subview in [yourView subviews]) //your view to find subview
{
if([subview isKindofClass[UIButton class]) //any UIElement of specific type here
{
UIButton *btn = (UIButton *)subview; //same UIElement mentioned for checking it
if(btn.tag == 123) //specific tah here
{
[arrSameViewTag addObject:subview]; //view found add in array
}
}
}
NSlog(#"arrSameViewTag : %#",arrSameViewTag)
For 1 level:
UIView *view;
for (int i = 0; i < viewToSearch.subviews.count; i++){
UIView *subview = viewToSearch.subviews[i];
if (subview.tag == tagToSeach){
view = subview;
break;
}
}
To search a view hierarchy with multiple levels:
__block UIView *view;
BOOL (^__block searchViewForTag)(UIView *,NSInteger) = ^(UIView *aView, NSInteger tag){
for (UIView *subview in aView.subviews){
if (subview.tag == tag){
view = subview;
return YES;
}
if (searchViewForTag(subview,tag)) return YES;
}
return NO;
};
NSInteger tagToSearchFor = 1;
searchViewForTag(viewToSearch,tagToSearchFor);
//Do something with view

multiple UIScrollViews on a UIView

Is there any way to add multiple UIScrollView's on a single UIView?
I made the UIView 2 parts. On the first part of view, I want to add one UIScrollView, and on the other, I want to add a second scrollview. The problem I'm facing is when I'm trying to zoom on the first, the second scroll is also responding.
How can I avoid that?
You should differentiate your scroll view by its tag property. Like :
assign tag to your scroll views
scrollView1.tag = 2001;
scrollView2.tag = 3001;
And then
- (void)scrollViewDidZoom:(UIScrollView *)myScrollView
{
if (myScrollView.tag == 2001)
{
//do stuff with scrollView1
}
else if (myScrollView.tag == 3001)
{
//do stuff with scrollView2
}
}
Use this Delegate method:-
Delegate methods send with it the object that sent the message (the UIScrollView in this case). So, all you have to do is check that against your instance variables of scrollView1 and scrollView2.
- (void)scrollViewDidZoom:(UIScrollView *)myScrollView {
if (myScrollView == scrollView1) {
//do stuff with scrollView1
} else if (myScrollView == scrollView2) {
//do stuff with scrollView2
}
}

How to check if a UIView is a subview of a parent view

I have an app which I add a subview to (and remove the same subview based on user interactions). I am looking for a way to check whether the subview is present or not at any given time.
For example:
In the current view (UIView *viewA) I add a subview (UIView *viewB). I then want a way of checking whether viewB is being displayed at any given time.
Sorry if this isn't very clear, it's quite hard to describe.
an UIView stores its superview and is accessible with the superview-property just try
if([viewB superview]!=nil)
NSLog(#"visible");
else
NSLog(#"not visible");
But the better approach is to use the hidden-property of UIView
I went through the same issue and consulted Apple Documentation and came up with this elegant solution:
if([self.childView isDescendantOfView:self.parentView])
{
// The childView is contained in the parentView.
}
I updated to Swift4, Thanks a lot to #Shinnyx and #thomas.
if viewB.superview != nil{
print("visible")
}
else{
print("not visible")
}
if selfView.isDescendant(of: self.parentView) {
print("visible")
}
else{
print("not visible")
}
func isDescendant(of: UIView)
Returns a Boolean value indicating whether the receiver is a subview of a given view or identical to that view.
Here's a method I put in the appDelegate so that I can display the entire subview hierarchy from any point.
// useful debugging method - send it a view and it will log all subviews
// can be called from the debugger
- (void) viewAllSubviews:(UIView *) topView Indent:(NSString *) indent {
for (UIView * theView in [topView subviews]){
NSLog(#"%#%#", indent, theView);
if ([theView subviews] != nil)
[self viewAllSubviews:theView Indent: [NSString stringWithFormat:#"%# ",indent]];
}
}
call it with a string with one character and it will indent for you. (i.e. [appDelegate viewAllSubviews:self.view Indent:#" "];)
I find it useful to clear the debug pane first, then call this from the debugger, and copy it into a text editor like BBEdit that will show the indents.
You can call it using the mainWindow's view and see everything on your screen.

pointInView always returns NO

I need to know if a point is inside at lease one of the views in a given set of views. For that, I used UIView's pointInView method, but it always returns NO. As an act of despair, I checked if the view's center point is inside the view and it also returned NO. This is the code I've used for that:
BOOL wasPointFound = NO;
NSArray *views = [view subviews];
for (UIView *curView in views)
{
if ([curView pointInside:curView.center withEvent:nil])
{
wasPointFound = YES;
break;
}
}
if (!wasPointFound)
NSLog(#"NO");
else
NSLog(#"YES");
Can anybody please tell me what I'm doing wrong?
Thanks,
PointInView is used to check if touch event is inside a view, that means that it is related to the window and not to the view. Taking curView.center is relative to the view therefore there is a good chance that using it will return false.
Try using CGPointMake(curView.frame.origin.x+curView.center.x,curView.frame.origin.y+curView.center.y)
This should return YES

Can I hook into UISearchBar's Clear Button?

I've got a UISearchBar in my interface and I want to customise the behaviour of the the small clear button that appears in the search bar after some text has been entered (it's a small grey circle with a cross in it, appears on the right side of the search field).
Basically, I want it to not only clear the text of the search bar (which is the default implementation) but to also clear some other stuff from my interface, but calling one of my own methods.
I can't find anything in the docs for the UISearchBar class or the UISearchBarDelegate protocol - it doesn't look like you can directly get access to this behaviour.
The one thing I did note was that the docs explained that the delegate method:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;
is called after the clear button is tapped.
I initially wrote some code in that method that checked the search bar's text property, and if it was empty, then it had been cleared and to do all my other stuff.
Two problems which this though:
Firstly, for some reason I cannot fathom, even though I tell the search bar to resignFirstResponder at the end of my method, something, somewhere is setting it back to becomeFirstResponder. Really annoying...
Secondly, if the user doesn't use the clear button, and simply deletes the text in the bar using the delete button on the keyboard, this method is fired off and their search results go away. Not good.
Any advice or pointers in the right direction would be great!
Thanks!
Found the better solution for this problem :)
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
if ([searchText length] == 0) {
[self performSelector:#selector(hideKeyboardWithSearchBar:) withObject:searchBar afterDelay:0];
}
}
- (void)hideKeyboardWithSearchBar:(UISearchBar *)searchBar{
[searchBar resignFirstResponder];
}
The answer which was accepted is incorrect. This can be done, I just figured it out and posted it in another question:
UISearchbar clearButton forces the keyboard to appear
Best
I've got this code in my app. Difference is that I don't support 'live search', but instead start searching when the user touches the search button on the keyboard:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
if ([searchBar.text isEqualToString:#""]) {
//Clear stuff here
}
}
Swift version handling close keyboard on clear button click :
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 {
performSelector("hideKeyboardWithSearchBar:", withObject:searchBar, afterDelay:0)
}
}
func hideKeyboardWithSearchBar(bar:UISearchBar) {
bar.resignFirstResponder()
}
You could try this:
- (void)viewDidLoad
{
[super viewDidLoad];
for (UIView *view in searchBar.subviews){
for (UITextField *tf in view.subviews) {
if ([tf isKindOfClass: [UITextField class]]) {
tf.delegate = self;
break;
}
}
}
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
// your code
return YES;
}
I would suggest using the rightView and rightViewMode methods of UITextField to create your own clear button that uses the same image. I'm assuming of course that UISearchBar will let you access the UITextField within it. I think it will.
Be aware of this from the iPhone OS Reference Library:
If an overlay view overlaps the clear button, however, the clear button always takes precedence in receiving events. By default, the right overlay view does overlap the clear button.
So you'll probably also need to disable the original clear button.
Since this comes up first, and far as I can see the question wasn't really adequately addressed, I thought I'd post my solution.
1) You need to get a reference to the textField inside the searchBar
2) You need to catch that textField's clear when it fires.
This is pretty simple. Here's one way.
a) Make sure you make your class a , since you will be using the delegate method of the textField inside the searchBar.
b) Also, connect your searchBar to an Outlet in your class. I just called mine searchBar.
c) from viewDidLoad you want to get ahold of the textField inside the searchBar. I did it like this.
UITextField *textField = [self.searchBar valueForKey:#"_searchField"];
if (textField) {
textField.delegate = self;
textField.tag = 1000;
}
Notice, I assigned a tag to that textField so that I can grab it again, and I made it a textField delegate. You could have created a property and assigned this textField to that property to grab it later, but I used a tag.
From here you just need to call the delegate method:
-(BOOL)textFieldShouldClear:(UITextField *)textField {
if (textField.tag == 1000) {
// do something
return YES;
}
return NO;
}
That's it. Since you are referring to a private valueForKey I can't guarantee that it will not get you into trouble.
Best solution from my experience is just to put a UIButton (with clear background and no text) above the system clear button and than connect an IBAction
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = #"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
Wasn't able to find a solution here that didn't use a private API or wasn't upgrade proof incase Apple changes the view structure of the UISearchBar. Here is what I wrote that works:
- (void)viewDidLoad {
[super viewDidLoad];
UITextField* textfield = [self findTextFieldInside:self.searchBar];
[textfield setDelegate:self];
}
- (UITextField*)findTextFieldInside:(id)mainView {
for (id view in [mainView subviews]) {
if ([view isKindOfClass:[UITextField class]]) {
return view;
}
id subview = [self findTextFieldInside:view];
if (subview != nil) {
return subview;
}
}
return nil;
}
Then implement the UITextFieldDelegate protocol into your class and overwrite the textFieldShouldClear: method.
- (BOOL)textFieldShouldClear:(UITextField*)textField {
// Put your code in here.
return YES;
}
Edit: Setting the delegate on the textfield of a search bar in iOS8 will produce a crash. However it looks like the searchBar:textDidChange: method will get called on iOS8 on clear.