reload UIwebview from tabBarItem - uiwebview

what i need is when someone clicked on tabBarItem 0 it will reload the page
I have tabBarController in my AppDelegate.m file
my problem is how to call webview in MainView.m to reload
i use code to check which tabBarItem was clicked and its work fine
(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSLog(#"TabItem pressed");
if (viewController == [TabBarController.viewControllers objectAtIndex:0]) {
NSLog(#"Tab 1 was pressed");
}
}

Are you trying to reload data?
If you are, then you can put something like:
if (viewController == [TabBarController.viewControllers objectAtIndex:0]) {
NSLog(#"Tab 1 was pressed");
[self.webView reloadData];
}

Related

How to calculate content height of WKWebView?

For UIWebView, we can get content height by this:
[webView stringByEvaluatingJavaScriptFromString:#"document.body.offsetHeight"]
But WKWebView doesn't have this method and webView.scrollView.contentSize.height is not right.
(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
}
Thank you for your help!
I solved this problem with KVO.
First, addObserver for WKWebView's scrollView's contentSize like this:
addObserver(self, forKeyPath: "webView.scrollView.contentSize", options: .New, context: nil)
And next, receive the change like this:
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if keyPath == "webView.scrollView.contentSize" {
if let nsSize = change[NSKeyValueChangeNewKey] as? NSValue {
let height = nsSize.CGSizeValue().height
// Do something here using content height.
}
}
}
It's easy to get webview's content height.
Don't forget to remove observer:
removeObserver(self, forKeyPath: "webView.scrollView.contentSize", context: nil)
I worte this line in deinit.
EDIT:
To get the best possible height calculation, in the end I did a few things to get the best height:
I set the size of the webview to be really large and then load the content.
Once the content is loaded (KVO notification) I resize the view using the method below.
I then run the size function again and I get a new size and resize my view once again.
The above got the most accurate results for me.
ORIGINAL:
I've tried the scroll view KVO and I've tried evaluating javascript on the document, using clientHeight, offsetHeight, etc...
What worked for me eventually is: document.body.scrollHeight. Or use the scrollHeight of your top most element, e.g. a container div.
I listen to the loading WKWebview property changes using KVO:
[webview addObserver: self forKeyPath: NSStringFromSelector(#selector(loading)) options: NSKeyValueObservingOptionNew context: nil];
And then:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if(object == self.webview && [keyPath isEqualToString: NSStringFromSelector(#selector(loading))]) {
NSNumber *newValue = change[NSKeyValueChangeNewKey];
if(![newValue boolValue]) {
[self updateWebviewFrame];
}
}
}
The updateWebviewFrame implementation:
[self.webview evaluateJavaScript: #"document.body.scrollHeight" completionHandler: ^(id response, NSError *error) {
CGRect frame = self.webview.frame;
frame.size.height = [response floatValue];
self.webview.frame = frame;
}];
You can get it like this:
self.webview.navigationDelegate = self;
- (void)webView:(WKWebView *)webview didFinishNavigation:(WKNavigation *)navigation{
// webview.scrollView.contentSize will equal {0,0} at this point so wait
[self checkIfWKWebViewReallyDidFinishLoading];
}
- (void)checkIfWKWebViewReallyDidFinishLoading{
_contentSize = _webview.scrollView.contentSize;
if (_contentSize.height == 0){
[self performSelector:#selector(WKWebViewDidFinishLoading) withObject:nil afterDelay:0.01];
}
}
WKWebView didn't finish loading, when didFinishNavigation is called - Bug in WKWebView?
WKWebView doesn't use delegation to let you know when content loading is complete.
You can create a new thread to check the status of WKWebView.
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
while(self.webView?.loading == true){
sleep(1)
}
dispatch_async(dispatch_get_main_queue(), {
print(self.webView!.scrollView.contentSize.height)
})
})
}

Cancel button in searchBar ios 8

tell me how to change the text on the Cancel button in searchBar?
Image: http://i.stack.imgur.com/8G1ZM.png
you can do that in this delegate method of UISearchBar
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[theSearchBar setShowsCancelButton:YES animated:NO];
for (UIView *subView in theSearchBar.subviews){
if([subView isKindOfClass:[UIButton class]]){
[(UIButton*)subViewsetTitle:#"Button Title"forState:UIControlStateNormal];
}
}
}
UPDATE
after a long way searching the only way i got working in swift
is to set a custom UIBarButtonItem but you will need to show the search bar on the navigation
in ViewDidLoad()
self.searchDisplayController?.displaysSearchBarInNavigationBar = true
and in Delegate Method
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
var barButton = UIBarButtonItem(title: "Button Title", style: UIBarButtonItemStyle.Done, target: self, action: "here")
self.searchDisplayController?.navigationItem.rightBarButtonItem = barButton
}
i hope that works with you
«Another UPDATE
as you said in comments you will need to localize your application, in your case you will only need to localize the storyBoard
first navigate to your project settings then info, under localizations click the + button and add your own languages then check only StoryBoard
and now you have localized your app but you might don't see the changes until you remove the app and install it again or if the device language is set to english you will need to write 2 lines of code to change the language manually here is it
var str:NSString = "ar" // ar stands for arabic you put here you own language small character
var myArray:NSArray = [str]
NSUserDefaults.standardUserDefaults().setObject(myArray, forKey: "AppleLanguages")
and your button will looks like this
if you want to know more about localization see this Internationalization Tutorial for iOS [2014 Edition]
if you still need help till me :)
There are 2 solutions.
set key/value
[self.searchController.searchBar setValue:#"취소" forKey:#"_cancelButtonText"];
This solution works well, but you have to know that _cancelButtonText property is not public property.(You can not find this property in the documentation page.)
And It's not sure that this solution can pass the apple review process. So, use solution 2 please.
use a delegate.
You can change cancel button title(iOS8) within willPresentSearchController method.
(Assume that the searchBar is in tableView headerView)
- (void)willPresentSearchController:(UISearchController *)searchController{
// You have to set YES showsCancelButton.
// If not, you can not change your button title when this method called
// first time.
self.searchController.searchBar.showsCancelButton = YES;
UIView* view=self.searchController.searchBar.subviews[0];
for (UIView *subView in view.subviews) {
if ([subView isKindOfClass:[UIButton class]]) {
// solution 1
UIButton *cancelButton = (UIButton*)subView;
[cancelButton setTitle:NSLocalizedString(#"취소", nil) forState:UIControlStateNormal];
}
}
}
Also, You can get infomation about the searchBar through a view debugging tools in Xcode. Set a breakPoint at the willPresentSearchController method.
Good luck.
Actually, the cancel button is not a top view in the Search bar. You should search for it recursively.
-(UIButton*)findButtonInView:(UIView*)v{
for (UIView *subView in v.subviews){
if([subView isKindOfClass:[UIButton class]]){
return (UIButton*)subView;
}else if([subView isKindOfClass:[UIView class]]){
UIButton* btn = [self findButtonInView:subView];
if(btn){
return btn;
}
}
}
return nil;
}
//...
UIButton* searchButton = [self findButtonInView:self.searchBar];

create a InApp browser implementation in iOS app

I have a requirement were in i have to create a view to display webpages with back, forward and refresh button in iOS app.
how to implement this functionality?
Use UIWebView.
Set the delegate to the view controller.
self.webView.delegate = self;
Other remarkable properties,
self.webView.scalesPageToFit = YES;
To load a URLRequest,
[self.webView loadRequest:theRequest];
or for only strings, use
[self.webView loadHTMLString:htmlString baseURL:nil];
And some delegates here. web view has its own history, you can use it by calling its back and forward methods.
#pragma mark - UIWebView delegate
// You can handle your own url scheme or let the web view handle it.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"url=%#, %#, %#",
request.URL, request.URL.query, request.URL.host);
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
if ([request.URL.scheme compare:#"customescheme"] == NSOrderedSame) {
if ([request.URL.host compare:kSomethingDotCom] == NSOrderedSame) {
[self mymethod];
} else if ([request.URL.host compare:kAnotherDotCom] == NSOrderedSame) {
[self method2];
} else {
NSLog(#"Unsupported service.");
}
return NO;
}
}
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[_activityIndicator stopAnimating];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
[_activityIndicator stopAnimating];
[Resources showAlert:#"Could not load." withTitle:#"Error!"];
NSLog(#"%#", [error localizedDescription]);
}
#pragma mark - Actions
- (IBAction)didPressBackButton:(id)sender
{
[_webView goBack];
}
- (IBAction)didPressForwardButton:(id)sender
{
[_webView goForward];
}
Similarly you can have the stop method. To refresh reload the request again. Before going back or forward you can check the methods canGoBack or canGoForward.
See docs at, http://developer.apple.com/library/ios/#documentation/uikit/reference/UIWebView_Class/Reference/Reference.html
It is a piece of cake, all you need is UIWebview component and its delegates and you need to create a custom navigation bar that is it.

How to enable "select all" in UIWebView in an IPhone App?

I am writing an IPhone application which embeds a UIWebView. There are various safari like features like navigation, etc. One of the tasks I am looking for is to present a "select all" option when the user selects on a piece of text on the web view. Currently, I only see a "copy" option. Is there an easy way to enable the "select all" menu item? I have ofcourse tried adding a menu item to the shared menu controller but that doesn't necessarily implement the original safari "select all" functionality. Any help and pointers will be very useful.
Thanks in advance.
The short answer is no, this is not possible.
You could do this by subclassing UIWebView and overriding (without adding anythings to the menu controller yourself):
-(BOOL) canPerformAction:(SEL)action withSender:(id)sender
And checking if the selector is selectAll:
Like this:
-(BOOL) canPerformAction:(SEL)action withSender:(id)sender {
if (action == #selector(selectAll:)) {
return YES;
} else {
return [super canPerformAction:action withSender:sender];
}
}
This will show the Select All option on the Hold Menu. However this isn't default behaviour of a webView, and while the app won't crash when you press Select All, pressing it will just do nothing.
You can't even create a selectAll method, and then select everything in webview, because the javascript method .select() doesn't work on Mobile Safari/UIWebView.
You can implement selectAll behavior for non-editable webView (equivalent to behavior at Apple's Mail.app) at runtime using the following category for UIWebView.
The main idea is to use the hint that UIWebBrowserView being the subview of UIWebView is the subclass of UIWebDocumentView which conforms to UITextInputPrivate protocol which is equivalent to public UITextInput protocol
// UIWebView+SelectAll.h
// Created by Alexey Matveev on 28.03.15.
// Copyright (c) 2015 Alexey Matveev. All rights reserved.
#interface UIWebView (SelectAll)
+ (void)setEnableSelectAll:(BOOL)enabled;
#end
#import "UIWebView+SelectAll.h"
#import <objc/runtime.h>
/*
UIWebDocumentView is the superclass for UIWebBrowserView.
UIWebDocumentView conforms UITextInputPrivate protocol which is identival to UITextInput
*/
static IMP canPerformActionWithSenderImp;
#implementation UIWebView (SelectAll)
#dynamic enableSelectAll;
- (BOOL)customCanPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(selectAll:)) {
return ! self.isSelectedAll;
}
else {
BOOL(*imp)(id, SEL, SEL, id) = (BOOL(*)(id, SEL, SEL, id))canPerformActionWithSenderImp;
return imp(self, #selector(canPerformAction:withSender:), action, sender);
}
}
- (void)selectAll:(id)sender
{
[self.browserView selectAll:sender];
}
- (UIView<UITextInput> *)browserView
{
UIView *browserView;
for (UIView *subview in self.scrollView.subviews) {
if ([subview isKindOfClass:NSClassFromString(#"UIWebBrowserView")]) {
browserView = subview;
break;
}
}
return (UIView<UITextInput> *)browserView;
}
- (BOOL)isSelectedAll
{
UITextRange *currentRange = self.browserView.selectedTextRange;
if ([self.browserView comparePosition:currentRange.start toPosition:self.browserView.beginningOfDocument] == NSOrderedSame) {
if ([self.browserView comparePosition:currentRange.end toPosition:self.browserView.endOfDocument] == NSOrderedSame) {
return YES;
}
}
return NO;
}
+ (void)setEnableSelectAll:(BOOL)enabled
{
SEL canPerformActionSelector = #selector(canPerformAction:withSender:);
if (!canPerformActionWithSenderImp) {
canPerformActionWithSenderImp = [self instanceMethodForSelector:canPerformActionSelector];
}
IMP newCanPerformActionWithSenderImp = enabled ? [self instanceMethodForSelector:#selector(customCanPerformAction:withSender:)] : canPerformActionWithSenderImp;
Method canPerformActionMethod = class_getInstanceMethod([self class], canPerformActionSelector);
class_replaceMethod([self class], canPerformActionSelector, newCanPerformActionWithSenderImp, method_getTypeEncoding(canPerformActionMethod));
}
#end
Of course, you can use global method swizzling for
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender;
in the standard way but it will affect all webViews in your project irreversably.

UISearchBar disable auto disable of cancel button

I have implemented a UISearchBar into a table view and almost everything is working except one small thing: When I enter text and then press the search button on the keyboard, the keyboard goes away, the search results are the only items shown in the table, the text stays in the UISearchBar, but the cancel button gets disabled.
I have been trying to get my list as close to the functionality of the Apple contacts app and when you press search in that app, it doesn't disable the cancel button.
When I looked in the UISearchBar header file, I noticed a flag for autoDisableCancelButton under the _searchBarFlags struct but it is private.
Is there something that I am missing when I setup the UISearchBar?
I found a solution. You can use this for-loop to loop over the subviews of the search bar and enable it when the search button is pressed on the keyboard.
for (UIView *possibleButton in searchBar.subviews)
{
if ([possibleButton isKindOfClass:[UIButton class]])
{
UIButton *cancelButton = (UIButton*)possibleButton;
cancelButton.enabled = YES;
break;
}
}
I had to tweak this a bit to get it work for me in iOS7
- (void)enableCancelButton:(UISearchBar *)searchBar
{
for (UIView *view in searchBar.subviews)
{
for (id subview in view.subviews)
{
if ( [subview isKindOfClass:[UIButton class]] )
{
[subview setEnabled:YES];
NSLog(#"enableCancelButton");
return;
}
}
}
}
There is two way to achieve this easily
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
// The small and dirty
[(UIButton*)[searchBar valueForKey:#"_cancelButton"] setEnabled:YES];
// The long and safe
UIButton *cancelButton = [searchBar valueForKey:#"_cancelButton"];
if ([cancelButton respondsToSelector:#selector(setEnabled:)]) {
cancelButton.enabled = YES;
}
}
You should go with the second one, it will not crash your application if Apple will change it in the background.
BTW i tested it from iOS 4.0 to 8.2 and no changes, also i use it in my Store approved application without any issues.
This is what made it to work on iOS 6 for me:
searchBar.showsCancelButton = YES;
searchBar.showsScopeBar = YES;
[searchBar sizeToFit];
[searchBar setShowsCancelButton:YES animated:YES];
Here's my solution, which works for all situations in all versions of iOS.
IE, other solutions don't handle when the keyboard gets dismissed because the user dragged a scroll view.
- (void)enableCancelButton:(UIView *)view {
if ([view isKindOfClass:[UIButton class]]) {
[(UIButton *)view setEnabled:YES];
} else {
for (UIView *subview in view.subviews) {
[self enableCancelButton:subview];
}
}
}
// This will handle whenever the text field is resigned non-programatically
// (IE, because it's set to resign when the scroll view is dragged in your storyboard.)
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
[self performSelector:#selector(enableCancelButton:) withObject:searchBar afterDelay:0.001];
}
// Also follow up every [searchBar resignFirstResponder];
// with [self enableCancelButton:searchBar];
As per my answer here, place this in your searchBar delegate:
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
dispatch_async(dispatch_get_main_queue(), ^{
__block __weak void (^weakEnsureCancelButtonRemainsEnabled)(UIView *);
void (^ensureCancelButtonRemainsEnabled)(UIView *);
weakEnsureCancelButtonRemainsEnabled = ensureCancelButtonRemainsEnabled = ^(UIView *view) {
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIControl class]]) {
[(UIControl *)subview setEnabled:YES];
}
weakEnsureCancelButtonRemainsEnabled(subview);
}
};
ensureCancelButtonRemainsEnabled(searchBar);
});
}
None of the answers worked for me at all. I'm targeting iOS 7. But I found an answer.
What I'm trying is something like the Twitter iOS app. If you click on the magnifying glass in the Timelines tab, the UISearchBar appears with the Cancel button activated, the keyboard showing, and the recent searches screen. Scroll the recent searches screen and it hides the keyboard but it keeps the Cancel button activated.
This is my working code:
UIView *searchBarSubview = self.searchBar.subviews[0];
NSArray *subviewCache = [searchBarSubview valueForKeyPath:#"subviewCache"];
if ([subviewCache[2] respondsToSelector:#selector(setEnabled:)]) {
[subviewCache[2] setValue:#YES forKeyPath:#"enabled"];
}
I arrived at this solution by setting a breakpoint at my table view's scrollViewWillBeginDragging:. I looked into my UISearchBar and bared its subviews. It always has just one, which is of type UIView (my variable searchBarSubview).
Then, that UIView holds an NSArray called subviewCache and I noticed that the last element, which is the third, is of type UINavigationButton, not in the public API. So I set out to use key-value coding instead. I checked if the UINavigationButton responds to setEnabled:, and luckily, it does. So I set the property to #YES. Turns out that that UINavigationButton is the Cancel button.
This is bound to break if Apple decides to change the implementation of a UISearchBar's innards, but what the hell. It works for now.
Here's a Swift 3 solution that makes use of extensions to get the cancel button easily:
extension UISearchBar {
var cancelButton: UIButton? {
for subView1 in subviews {
for subView2 in subView1.subviews {
if let cancelButton = subView2 as? UIButton {
return cancelButton
}
}
}
return nil
}
}
Now for the usage:
class MyTableViewController : UITableViewController, UISearchBarDelegate {
var searchBar = UISearchBar()
func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.tableHeaderView = searchBar
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
DispatchQueue.main.async {
if let cancelButton = searchBar.cancelButton {
cancelButton.isEnabled = true
cancelButton.isUserInteractionEnabled = true
}
}
}
}
For Monotouch or Xamarin iOS I have the following C# solution working for iOS 7 and iOS 8:
foreach(UIView view in searchBar.Subviews)
{
foreach(var subview in view.Subviews)
{
//Console.WriteLine(subview.GetType());
if(subview.GetType() == typeof(UIButton))
{
if(subview.RespondsToSelector(new Selector("setEnabled:")))
{
UIButton cancelButton = (UIButton)subview;
cancelButton.Enabled = true;
Console.WriteLine("enabledCancelButton");
return;
}
}
}
}
This answer is based on David Douglas solution.
A more complete answer:
since iOS 7, there is an additional level of subviews under the searchBar
a good place to enable the cancel button is in searchBarTextDidEndEditing
.
extension MyController: UISearchBarDelegate {
public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
DispatchQueue.main.async {
// you need that since the disabling will
// happen after searchBarTextDidEndEditing is called
searchBar.subviews.forEach({ view in
view.subviews.forEach({ subview in
// ios 7+
if let cancelButton = subview as? UIButton {
cancelButton.isEnabled = true
cancelButton.isUserInteractionEnabled = true
return
}
})
// ios 7-
if let cancelButton = subview as? UIButton {
cancelButton.isEnabled = true
cancelButton.isUserInteractionEnabled = true
return
}
})
}
}
}
Time passes, but the problem is still there...
Elegant Swift 5/iOS 13 solution:
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
for case let cancelButton as UIButton in searchBar.subviews {
cancelButton.isEnabled = true
}
}

Resources