Compare self.parentViewController to a given UIViewController - objective-c

Is it possible? That is, can I do something like this, which doesn't work...
if (self.parentViewController == CustomViewController) {
// do something
} else {
// do something else
}
...to make, for example, a Settings panel look more like either of its possible parent controllers? (Example again, if the menu is soft and light but the gameplay is kinda dark, it'd be convenient to say "if your parent is A look like this, but if your parent is B, look like this")
If it definitely can't be done, or if comparing the parentViewController to something else is dangerous/messy, I'll just set a flag fromView and code according to that.

You could go with if([self.parentViewController isKindOfClass:[CustomViewController class]])

self.parentViewController returns NavigationController so it doesn't work, so I found other way, this worked for me:
unsigned long currentVCIndex = [self.navigationController.viewControllers indexOfObject:self.navigationController.topViewController];
//previous view controller
UIViewController *view = (UIViewController *)[self.navigationController.viewControllers objectAtIndex:currentVCIndex - 1];
if([view isKindOfClass:[CustomViewController class]])
{
[self runSomething];
}

Related

How do I iterate over all controls in a View

I have a static table view controller. Within some of the cells, I have text boxes. I would like to enable or disable all the text boxes in one go. I know I could do something like
self.nameTextField.Enabled = NO;
self.ageTextField.Enabled = NO;
self.hairColorTextField.Enabled = NO;
But there has to be something more elegant. Something like
for (UIControl* control in self.allChildControls) { // <-- I totally just made that up.
if ([control isKindOfClass:[UITextField class]]) {
control.Enabled = NO;
}
}
I don't think I am asking the right question...
You can use the subviews property od UIView. It contains all child UI elements.
#property(nonatomic, readonly, copy) NSArray *subviews
UIView Documentation
for (UIView *subview in self.view.subviews) {
//check by class or tag
}
If you have a static tableviewController, I am assuming you aren't allowing the user to add/delete cells. If this is the case, your question is simple. You just need to add an outlet to each of the UITextField objects and toggle it's userInteractionEnabled property to no.
self.myTextField.userInteractionEnabled = NO;
self.mySecondTextField.userInteractionEnabled = NO;
Hope this helps :)

Get the object at top of uiview hierarchy

I have a known location (CGPoint) and I want to query the view (UIView) for the object under it or that contains it, whether its the view itself, or a button inside that, or a label or any other instance
I then want to grab that object, find out it type, and call any methods that happen when its tapped by the user.
I tried calling touchesBegan on the view, but theres no way to create touch events or uievents it seems... correct me if I'm wrong.
I'm thinking there might be a way to do this with hitTest, but I'm unsure.
hit test will do it.
If you don't want something returned, turn off userInteractionEnabled
//Send touch down event
//Now, this is a bit hacky. Theres no public contrustors for UITouch or UIEvent, thus calling touches*: on the view is not possible. Instead, I search the view underneath it (with Hit Test) and call actions on that.
//Note. You need to allow for each type of object below, for the scope of the demo, I've allowed for only UIView and UIButton.
UIView *v = [[self view] hitTest:pointer.frame.origin withEvent:nil]; //origin is top left, point of pointer.
NSLog(#"%#", [v class] );
if([v isKindOfClass:[UIButton class]]){
selectedButton = (UIButton *)v;
}
else if ([v isKindOfClass:[UIView class]] && [[v backgroundColor] isEqual:[UIColor redColor]]){
selectedView = v;
dragLabel.text = #"You are dragging me!";
dragPoint = pointer.frame.origin; //record this point for later.
}
else {
NSLog (#"touched but no button underneath it");
}

highlight NSSearchField

I have a NSWindow which consist a NSView which appear only on some specific occasion this NSView consist a NSSearchField I want if there is data on clipboard it will appear in searchField otherwise it would be empty.I am able to do this what I want is if there is Data in SearchField it should be in focus I tried it this way:
In NSViewController class there is a function which returns NSSearchField which is a class variable
-(NSSearchField*)getSearchField
{
return searchField;
}
In NSWindowController class I am making it first responder where pSearchContact is instance variable of NSViewController class
[[self window] makefirstResponder:[pSearchContact getSearchField]];
It is running smoothly but I don't know why searchField is not getting focus
Is their something like searchField will become first responder only if it is a part of NSWindow because in my case searchField is in NSView which is in NSWindow.
Thanks in Advance
Your question is a little confusing but I think what you're asking to boils down to "why can't I ever make my search field the focus?".
That one line of code:
[[self window] makefirstResponder:[pSearchContact getSearchField]];
has a little too much going on with it for my comfort (i.e. I wouldn't embed so much functionality - any pieces of which could go wrong or haywire - into one line of code).
How about doing something like this:
NSWindow * myWindow = [self window];
if(myWindow)
{
if(pSearchContact)
{
NSResponder * ourSearchField = [pSearchContact getSearchField];
if(ourSearchField)
{
[myWindow makeFirstResponder: ourSearchField];
} else {
NSLog( #"ourSearchfield is nil; why?" );
}
} else {
NSLog( #"pSearchContact is nil; why?" );
}
} else {
NSLog( #"myWindow is nil; why?" );
}
This might also allow you to narrow down on why the focus setting isn't working for you.
A late response, but I was having a similar issue which was fixed by unchecking refusesFirstResponder in Xcode/Interface Builder.

Show a preloaded search results?

I have a non-tableview view with a searchbar in it, and while it works perfectly, the search display controller hides the table view and overlays a dark dimmed view when an empty string is in the searchbar. I want it to show a preloaded data when the empty string is in the searchbar instead of hiding the table view and overlaying the dark dimmed view underneath the searchbar. Just like how the Google search bar in Safari for iOS works.
I found a similar question asked on stackoverflow before:
UISearchDisplayController - how to preload searchResultTableView, I couldn't really get it to work.
I have no problem getting the preloaded data and setting the current data to it, but I'm not sure how to prevent the displaycontroller from removing the searchResultsTableView.
Thanks in advance.
I finally found a way to do this.
I found out that the searchDisplayController simply removes the searchResultsTableView from the superview, so I just added the table view back into the superview whenever the display controller tried to hide the table view:
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
// add the tableview back in
[self.view addSubview:self.searchDisplayController.searchResultsTableView];
}
and then I also have to show the tableview the first time the searchbar is clicked, so I did:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
// after the data has been preloaded
self.searchResults = self.allItems;
[self.searchDisplayController.searchResultsTableView reloadData];
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
[self.view addSubview:self.searchDisplayController.searchResultsTableView];
}
For me, 'allItems' is where I stored all the searchable items and 'searchResults' is where the filtered items (after the search) is stored. And of course, you would have to preload the items (e.g. search history) before reloading the data.
I don't know if this is a nice way or not to do it in terms of the performance and what not, but it worked perfectly for me, and I hope this could be useful for other people as well. Please comment if there is a better way to do this.
After hours and hours I finally figured out a solution that works in iOS 7
Just implement the following two methods in your UISearchDisplayDelegate
-(void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {
// We need to prevent the resultsTable from hiding if the search is still active
if (self.searchDisplayController.active == YES) {
tableView.hidden = NO;
}
}
When the search starts, the searchResultsTableView is being hidden automatically, so we need to unhide it again
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
controller.searchResultsTableView.hidden = NO;
// Then we need to remove the semi transparent overlay which is here
for (UIView *v in [[[controller.searchResultsTableView superview] superview] subviews]) {
if (v.frame.origin.y == 64) {
[v setHidden:YES];
}
}
}
I found a much better solution to this issue, and it seems to work perfectly on iOS 6 and 7. While it is still a hack, its a much cleaner and future proof hack than the above. The other solutions do not work consistently and prevent some UISearchDisplayDelegate methods from ever firing! Further I had complex insetting issues which I could not resolve with the above methods. The main issue with the other solutions is that they seriously confuse the internals of the UISearchDisplayController. My solution is based on the observation that UISearchDisplayContoller is a UISearchbarDelegate and that the automatic undimming & showing of results table can be triggered by simulating a keypress in the search field! So:
- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
if ([controller respondsToSelector: #selector(searchBar:textDidChange:)])
[(id<UISearchBarDelegate>)controller searchBar: controller.searchBar textDidChange: #" "];
}
This code is future proof against crashing by checking it responds to the UISearchbarDelegate method, and sends space #" " to trick the UISearchDisplayController into thinking user has typed a letter.
Now if the user types something and then erases it, the table will dim again. The other solutions try to work around this by doing something in the searchDisplayController:didHideSearchResultsTableView: method. But this doesn't make sense to me, as surely when you cancel the search it will need to truly hide your results table and you may need to run code in this case. My solution for this part is to subclass (note you could probably use a Method Swizzled Category to make it work everywhere if needed in your project):
// privately declare protocol to suppress compiler warning
#interface UISearchDisplayController (Super) <UISearchBarDelegate>
#end
// subclass to change behavior
#interface GMSearchDisplayController : UISearchDisplayController
#end
#implementation GMSearchDisplayController
- (void) searchBar: (UISearchBar *) searchBar textDidChange: (NSString *) searchString
{
if (searchString.length == 0)
searchString = #" ";
if ([super respondsToSelector: #selector(searchBar:textDidChange:)])
[super searchBar: searchBar textDidChange: searchString];
}
#end
This code works by intercepting the textDidChange delegate method and changing nil or empty strings in to space string #" " preventing the normal hiding/dimming that occurs on an empty search bar. If you are using this second bit of code, then you could modify the first bit to pass a nil instead of #" " as this second bit will do the needed conversion to #" " for you.
In my own project, I needed to handle the case that user does type a space, so instead of #" " above I used a defined token:
// arbitrary token used internally
#define SEARCH_PRELOAD_CONDITIONAL #"_#preresults#_"
And then handle it internally by converting it back to nil string:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
if ([searchString isEqualToString: SEARCH_PRELOAD_CONDITIONAL])
searchString = nil;
}
Enjoy! :)
This works in iOS 8:
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
self.searchDisplayController.searchResultsTableView.hidden = NO;
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
self.searchDisplayController.searchResultsTableView.hidden = NO;
[self.searchDisplayController.searchResultsTableView.superview.superview bringSubviewToFront:self.searchDisplayController.searchResultsTableView.superview];
CGRect frame = self.searchDisplayController.searchResultsTableView.frame;
self.searchDisplayController.searchResultsTableView.frame = CGRectMake(frame.origin.x, 64, frame.size.width, frame.size.height);
}
When you start searching this method gets called. Add the searchResultsTableView and unhide it. It would then display your already preloaded data. I must have your data preloaded in order for this to work.
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
self.searchDisplayController.searchResultsTableView.frame = testFrame;
[self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
// [self.view addSubview:self.searchDisplayController.searchResultsTableView];
controller.searchResultsTableView.hidden = NO;
}
-(void) searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView
{
CGRect testFrame = CGRectMake(0, self.notesSearchBar.frame.size.height, self.notesSearchBar.frame.size.width, self.view.frame.size.height - self.notesSearchBar.frame.size.height);
self.searchDisplayController.searchResultsTableView.frame = testFrame;
[self.notesSearchBar.superview addSubview:self.searchDisplayController.searchResultsTableView];
// [self.view addSubview:self.searchDisplayController.searchResultsTableView];
controller.searchResultsTableView.hidden = NO;
}
-(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
controller.searchResultsTableView.hidden = YES;
}
iOS 9 working code.
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
// Bring the search table view to the view's front
self.searchDisplayController.searchResultsTableView.hidden = NO;
[self.searchDisplayController.searchResultsTableView.superview bringSubviewToFront:self.searchDisplayController.searchResultsTableView];
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {
// We need to prevent the resultsTable from hiding if the search is still active
if (self.searchDisplayController.active == YES) {
tableView.hidden = NO;
}
}
Swift 2.0+ version
func searchDisplayControllerDidBeginSearch(controller: UISearchDisplayController) {
controller.searchResultsTableView.hidden = false
controller.searchResultsTableView.superview!.bringSubviewToFront(controller.searchResultsTableView)
}
func searchDisplayController(controller: UISearchDisplayController, didHideSearchResultsTableView tableView: UITableView) {
if ((searchDisplayController?.active) != nil) {
tableView.hidden = false
}
}

how to make textfield changes appear in two different views?

I have a class where I used the textfield delegate method "shouldChangeCharactersInRange". If the user types something in a textfield in one view, I want those changes to appear in a textfield in a different view.
Right I now, I have two xib files with the same file's owner and I make a connection (in IB) in each xib file to my textfield (which i declared as an IBOutlet). It's an Ipad app so I switch between views when user rotates device.
It's not working yet so I must be missing something? Could someone please help me! thank you!
If this question is not clear, please let me know.
Wherever your shouldChangeCharactersInRange: method is implemented, if you have a reference to both of the textfields, what you can do is set the text of both. So where right now you have something like:
- (BOOL)textField: (UITextField *)textField shouldChangeCharactersInRange: (NSRange)range replacementString: (NSString *)string
{
if (textField.text.length >= MAX_LENGTH && range.length == 0)
{
return NO;
}
else
{
return YES;
}
}
You want to add something like:
- (BOOL)textField: (UITextField *)textField shouldChangeCharactersInRange: (NSRange)range replacementString: (NSString *)string
{
if (textField.text.length >= MAX_LENGTH && range.length == 0)
{
return NO;
}
else
{
[myFirstTextField setText:string];
[mySecondTextField setText:string];
return YES;
}
}
And if you set both textFields to delegate to that single function, you don't even need to care who delegated to you there. The action you want to take is the same no matter who got text entered in them.