chogath chogath - 5 months ago 37
Objective-C Question

Create Custom UIView with Custom WebView and UISearchContoller

I'm working on an iOS app and I created an UIViewController where I put my components and it works fine .
Now I'm trying to create a Custom UIView and to put my webview and my searchcontroller into . I spent a lot of time without success .

Here is my .m file and I hope some one can help me with :

#import "HomeViewController.h"


#define widthtScreen [UIScreen mainScreen].bounds.size.width
#define heightScreen [UIScreen mainScreen].bounds.size.height


@interface HomeViewController () <UISearchResultsUpdating,UISearchBarDelegate,UIBarPositioningDelegate,UITableViewDataSource,UITableViewDelegate,MapWebViewDelegate>

@property(strong,nonatomic) MapWebView *webView;

@property (nonatomic) UIButton *btnGeolocate;


@property (nonatomic, strong) UISearchController *searchController;

@end

@implementation HomeViewController{
NSMutableArray *placesList;
BOOL isSearching;
}



-(void)loadView
{
[super loadView];
self.webView = [[MapWebView alloc] initWithFrame:CGRectMake(0, -100, widthtScreen, heightScreen+100)];
self.webView.mapWebViewDelegate = self;
[self.view addSubview:self.webView];
}

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationItem.hidesBackButton = YES;
mainDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
placesList = [[NSMutableArray alloc] init];
[self initializeSearchController];
mainDelegate.webView = self.webView;


self.btnGeolocate = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width-75,550,60,60)];
self.btnGeolocate.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[self.btnGeolocate setBackgroundImage:[UIImage imageNamed:@"geo.png"]
forState:UIControlStateNormal];
[self.btnGeolocate addTarget:self action:@selector(btnGeolocatePressed:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.btnGeolocate];

mainDelegate.btnZoomIn = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width-80,620,30,30)];
mainDelegate.btnZoomIn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[mainDelegate.btnZoomIn setBackgroundColor:[UIColor blackColor]];
[mainDelegate.btnZoomIn addTarget:self action:@selector(btnZoomInPressed:) forControlEvents:UIControlEventTouchUpInside];
mainDelegate.btnZoomIn.tag=1;
UIImage *btnImage = [UIImage imageNamed:@"plus.png"];
[mainDelegate.btnZoomIn setImage:btnImage forState:UIControlStateNormal];
[self.view addSubview:mainDelegate.btnZoomIn];

mainDelegate.btnZoomOut = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width-40,620,30,30)];
mainDelegate.btnZoomOut.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[mainDelegate.btnZoomOut setBackgroundColor:[UIColor blackColor]];
[mainDelegate.btnZoomOut addTarget:self action:@selector(btnZoomOutPressed:) forControlEvents:UIControlEventTouchUpInside];
mainDelegate.btnZoomOut.tag=1;
UIImage *btnImage2 = [UIImage imageNamed:@"minus.png"];
[mainDelegate.btnZoomOut setImage:btnImage2 forState:UIControlStateNormal];
[self.view addSubview:mainDelegate.btnZoomOut];
}

- (BOOL)slideNavigationControllerShouldDisplayLeftMenu
{
return YES;
}

- (BOOL)slideNavigationControllerShouldDisplayRightMenu
{
return YES;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(@"number :%lu",(unsigned long)[placesList count]);
return [placesList count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
SuggestResultObject *sro = [SuggestResultObject new];
sro = [placesList objectAtIndex:indexPath.row];
cell.textLabel.text = sro.textPlace;
return cell;
}

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
self.navigationItem.leftBarButtonItem = nil;
self.navigationItem.rightBarButtonItem =nil;
return true;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.navigationItem.leftBarButtonItem = mainDelegate.leftBarButtonItem;
self.navigationItem.rightBarButtonItem = mainDelegate.rightBarButtonItem;
SuggestResultObject *sro = [SuggestResultObject new];
sro = [placesList objectAtIndex:indexPath.row];
self.searchController.active = false;
NSString *function = [[NSString alloc] initWithFormat: @"MobileManager.getInstance().moveToLocation(\"%@\",\"%@\")", sro.latPlace,sro.lonPlace];
[_webView evaluateJavaScript:function completionHandler:nil];
}



- (void)jsRun:(NSString *) searchText {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *function = [[NSString alloc] initWithFormat: @"MobileManager.getInstance().setSuggest(\"%@\")", searchText];
[_webView evaluateJavaScript:function completionHandler:nil];
});
}

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
isSearching = YES;
}

- (void)initializeSearchController {
UITableViewController *searchResultsController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
searchResultsController.tableView.dataSource = self;
searchResultsController.tableView.delegate = self;
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
self.definesPresentationContext = YES;
self.searchController.hidesNavigationBarDuringPresentation = false;
self.searchController.accessibilityElementsHidden= true;
self.searchController.dimsBackgroundDuringPresentation = true;
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.navigationItem.titleView = self.searchController.searchBar;
self.searchController.searchResultsUpdater = self;
self.searchController.searchBar.delegate = self;
}

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
self.navigationItem.leftBarButtonItem = mainDelegate.leftBarButtonItem;
self.navigationItem.rightBarButtonItem = mainDelegate.rightBarButtonItem;
NSLog(@"Cancel clicked");
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[placesList removeAllObjects];
}


-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
[placesList removeAllObjects];
if([searchController.searchBar.text length] != 0) {
isSearching = YES;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self jsRun:searchController.searchBar.text];
}
else {
isSearching = NO;
[((UITableViewController *)self.searchController.searchResultsController).tableView reloadData];
}
}

-(void) btnGeolocatePressed : (id) sender{
}

-(void) btnZoomInPressed : (id) sender{
[_webView evaluateJavaScript:@"MobileManager.getInstance().zoomIn();" completionHandler:nil];
}

-(void) btnZoomOutPressed : (id) sender{
[_webView evaluateJavaScript:@"MobileManager.getInstance().zoomOut();" completionHandler:nil];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)searchResult:(NSArray*)dataArray{
SuggestResultObject *sro = [SuggestResultObject new];
sro.textPlace = [dataArray objectAtIndex:0];
sro.lonPlace = [dataArray objectAtIndex:1];
sro.latPlace = [dataArray objectAtIndex:2];
[placesList addObject:sro];
[((UITableViewController *)self.searchController.searchResultsController).tableView reloadData];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
@end

Answer

Screenshots (Not sure how to scale images on stackoverflow.. OSX takes images in Retina format and they're fairly large! Sorry in advance!):

http://i.imgur.com/15qxDpc.png

http://i.imgur.com/QHduP07.png

How it works? Create a UIView and two sub-views: UITableView and UIWebView. Constrain them properly.

Create the UISearchController with a nil SearchResultsController as the parameter to the init method.

This lets the search controller know that the results will be displayed in the current controller/view.

Next we setup the delegates for the UISearchController and create the function for filtering.

Now that we have the view created, we need a UIViewController to test it. We can either add the search bar to the tableView header OR to the NavigationController if there is one..

Below, I have chosen to add it to the UINavigationController and I told the UISearchController to NOT HIDE the navigation bar on presentation.

That way, the results are displayed in the current view without hiding the navigation bar.

You can then use the webview which is hidden and offscreen to do whatever javascript searches you are using it for..

However, a better idea would be to use JSContext to execute Javascript instead of a UIWebView. The advantage of the UIWebView is that you can parse HTML and modify DOM which the JSContext doesn't allow.

Anyway..

Here is the code I wrote for a UIView that contains a UISearchController and a UIWebView.. and then to add it to a UIViewController that is embedded in a UINavigationController.

//
//  SearchView.h
//  StackOverflow
//
//  Created by Brandon T on 2016-06-26.
//  Copyright © 2016 XIO. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface SearchView : UIView
- (UISearchBar *)getSearchBar;
@end


//
//  SearchView.m
//  StackOverflow
//
//  Created by Brandon T on 2016-06-26.
//  Copyright © 2016 XIO. All rights reserved.
//

#import "SearchView.h"

#define kTableViewCellIdentifier @"kTableViewCellIdentifier"

@interface SearchView() <UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating>
@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) UISearchController *searchController;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSArray *dataSource;
@property (nonatomic, strong) NSArray *searchResults;
@end

@implementation SearchView

- (instancetype)init {
    if (self = [super init]) {

        [self setupData];
        [self initControls];
        [self themeControls];
        [self registerCells];
        [self doLayout];
    }
    return self;
}

- (UISearchBar *)getSearchBar {
    return _searchController.searchBar;
}

- (void)setupData {
    //Begin fake data

    _dataSource = @[@"Cat", @"Dog", @"Bird", @"Parrot", @"Rabbit", @"Racoon", @"Rat", @"Hamster", @"Pig", @"Cow"];

    //End fake data


    _searchResults = [_dataSource copy];
}

- (void)initControls {
    _webView = [[UIWebView alloc] init];
    _searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
    _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
}

- (void)themeControls {
    [_webView setHidden:YES];

    [_tableView setDelegate:self];
    [_tableView setDataSource:self];

    _searchController.searchResultsUpdater = self;
    _searchController.dimsBackgroundDuringPresentation = false;
    _searchController.definesPresentationContext = true;
    _searchController.hidesNavigationBarDuringPresentation = false;
}

- (void)registerCells {
    [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kTableViewCellIdentifier];
}

- (void)doLayout {
    [self addSubview:_webView];
    [self addSubview:_tableView];

    NSDictionary *views = @{@"webView":_webView, @"tableView": _tableView};
    NSMutableArray *constraints = [[NSMutableArray alloc] init];

    [constraints addObject:[NSString stringWithFormat:@"H:|-(%d)-[webView]-(%d)-|", 0, 0]];
    [constraints addObject:[NSString stringWithFormat:@"H:|-(%d)-[tableView]-(%d)-|", 0, 0]];

    [constraints addObject:[NSString stringWithFormat:@"V:|-(%d)-[webView(%d)]-(%d)-[tableView]-(%d)-|", -100, 100, 0, 0]];

    for (NSString *constraint in constraints) {
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraint options:0 metrics:nil views:views]];
    }

    for (UIView *view in self.subviews) {
        [view setTranslatesAutoresizingMaskIntoConstraints:NO];
    }
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _searchController.active && _searchController.searchBar.text.length > 0 ? [_searchResults count] : [_dataSource count];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 50;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kTableViewCellIdentifier forIndexPath:indexPath];

    if (_searchController.active && _searchController.searchBar.text.length > 0) {
        cell.textLabel.text = _searchResults[indexPath.row];
    }
    else {
        cell.textLabel.text = _dataSource[indexPath.row];
    }

    return cell;
}


- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
    [self filterResults:searchController.searchBar.text scope:nil];
}

- (void)filterResults:(NSString *)searchText scope:(NSString *)scope {
    _searchResults = [_dataSource filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id  _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {

        NSString *object = [evaluatedObject uppercaseString];
        return [object rangeOfString:[searchText uppercaseString]].location != NSNotFound;
    }]];

    [_tableView reloadData];
}

@end

Then I tested it with the below UIViewController which is embedded in a UINavigationController..

//
//  ViewController.m
//  StackOverflow
//
//  Created by Brandon T on 2016-06-26.
//  Copyright © 2016 XIO. All rights reserved.
//

#import "ViewController.h"
#import "SearchView.h"


@interface ViewController ()
@property (nonatomic, strong) SearchView *searchView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self initControls];
    [self themeControls];
    [self doLayout];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}


- (void)initControls {
    _searchView = [[SearchView alloc] init];
}

- (void)themeControls {
    self.edgesForExtendedLayout = UIRectEdgeNone;
    self.navigationItem.titleView = [_searchView getSearchBar];
}

- (void)doLayout {
    [self.view addSubview:_searchView];

    NSDictionary *views = @{@"searchView":_searchView};
    NSMutableArray *constraints = [[NSMutableArray alloc] init];

    [constraints addObject:[NSString stringWithFormat:@"H:|-%d-[searchView]-%d-|", 0, 0]];
    [constraints addObject:[NSString stringWithFormat:@"V:|-%d-[searchView]-%d-|", 0, 0]];

    for (NSString *constraint in constraints) {
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraint options:0 metrics:nil views:views]];
    }

    for (UIView *view in self.view.subviews) {
        [view setTranslatesAutoresizingMaskIntoConstraints:NO];
    }
}
@end
Comments