Multiple UIWebViews question - iphone

I'm really not good with these protocols, especially because this is really my first time using them. I have two UIWebViews in the same view: webView and webView2. How do I change this line of code to work for webView2?
-(void)webViewDidStartLoad:(UIWebView *)webView {
I tried changing "webView" to "webView2", but Xcode said that I had the same line twice, so obviously this won't work. What should I do? Thanks for your help!
Btw, I'm thinking I have to add an IF statement within here, but what should it be?

The delegate method passes in a parameter defining which web view it’s coming from for exactly this reason.
- (void)webViewDidStartLoad:(UIWebView *)theWebView
{
if(theWebView == webView)
{
// do something
} else if(theWebView == webView2)
{
// do something else
}
}

Well the same method will be called, but the ref of the webview is passed in,
So you can keep a reference to both your webviews in your delegate and say something like if(webview1==webview) which will evaluate to yes only if the webview ref passed in the delegate method is webview1 so you can figure out which webview is calling the delegate using the if statement

I think you want:
- (void)webViewDidStartLoad:(UIWebView *)webView; {
if(webView == webView1){
// use the first webview here.
}
if(webView == webView2){
// use the second webview here.
}
}
Hope that Helps!

Related

block delegate methods of class in iphone

I am having a problem I am working on a class which is subclass of UITextField.
Which will be used in many classes further.
But I don't want to let user to use it's delegate methods in any way.
Is there any way to do this ?
Override setDelegate: so that it throws an exception or logs an instruction on what to do. That way your API users will know what's actually going on.
-(void) setDelegate: (id <UITextFieldDelegate>) delegate
{
NSLog(#"*** Use the blocks API instead of calling %s", __PRETTY_FUNCTION__);
[self doesNotRecognizeSelector: _cmd];
}
Override the -setDelegate: method such that it never actually sets a delegate. You can just provide an empty method that fails to call super:
-(void) setDelegate:(id<UITextFieldDelegate>) delegate
{
// this method intentionally empty to prevent a delegate from ever being set
}

Strange behaviour in viewWillAppear

I have a TabBar Controller with some tab bar item in it.
The first time that a user tap on a tab bar item, I want that a alertview is opened, so that the user can read some little instruction tips.
I have a global variable (say CONFIG), that hold some boolean valeus (CONFIG.tip1AlreadySeen, CONFIG.tip1AllreadySeen, etc.). All these boolean values are initializated to NO.
When the user tap a tab bar item, the viewWillAppear method in its viewcontroller is executed. In this method I put a code like this one:
-(void) viewVillAppear: (BOOL) animated {
extern CONFIG; // <- it's not the actual code but it indicates that a global variable must be used
[super viewWillAppear: animated];
if(CONFIG.tip1AlreadySeen == NO) {
CONFIG.tip1AlreadySeen = YES;
// code for showing the alertview
}
}
The strange thing is that this piece of code works perfectly in one viewcontroller but doesn't work in one another.
With some debug, I fidd out that in the another viewcontroller the code is executed but the assigment CONFIG.tipAlreadySeen = YES doesn't modify the actual value of CONFIG.tipAlreadySeen. This value is still NO. Unbelievable!!!
A little workaround was using the viewDidAppear method for changing the value:
-(void) viewVillAppear: (BOOL) animated {
extern CONFIG; // <- it's not the actual code but it indicates that a global variable must be used
[super viewWillAppear: animated];
if(CONFIG.tip1AlreadySeen == NO) {
// code for showing the alertview
}
}
-(void) viewDidAppear: (BOOL) animated {
extern CONFIG;
CONFIG.tip1AlreadySeen = YES;
}
...But I really did not understand what happened!!! Someone of you could explain this behaviour?
Thanks in advance!
Marco
Why must this be global and not contained in the view controller itself? Just a simple BOOL #property on your view controller that is toggled. And, to maintain this persistent across multiple runs of your application, save out the result to NSUserDefaults, which you in turn check each time you init your view controller.

Custom Detail Image View from Three20

I am pretty new with Three20. I have followed ray wenderlich's nice introduction to three20 and the examples within the three20 framework. When I click on a thumbnail in a thumbnail view (subclass of TTThumbsViewController) to launch a Details view, a standard Details image view (deployed by TTPhotoViewController or its super class). I would like to use my own implementation of a Details View instead of the default. I put the following code when I initiated the subclass of TTThumbsViewController and TTThumbsViewControllerDelegate method:
- (id)initWithDelegate:(id<TTThumbsViewControllerDelegate>)delegate {
[super initWithDelegate:delegate];
return self;
}
- (void)thumbsViewController: (TTThumbsViewController*)controller
didSelectPhoto: (id<TTPhoto>)photo {
[navigationController.pushViewController:photoDetailViewController
animated:Yes];
}
But the default TTPhotoViewController view still prevail. When I put a NSLog in the delegate method. I coud see the method was called. I think there is another delegate someone already set in TTThumViewController? Can someone recommend a way to display my detail photo view? Is there another thumbs view controller I can use? Any suggestion will be greatly appreciated.
I'm really new to all of this (coding, etc.) but I'll share what I've found. By looking up the definition of ttthumbsviewcontroller, I was able to find the following method(wrong term?):-
- (void)thumbsTableViewCell:(TTThumbsTableViewCell*)cell didSelectPhoto:(id<TTPhoto>)photo {
[_delegate thumbsViewController:self didSelectPhoto:photo];
BOOL shouldNavigate = YES;
if ([_delegate respondsToSelector:#selector(thumbsViewController:shouldNavigateToPhoto:)]) {
shouldNavigate = [_delegate thumbsViewController:self shouldNavigateToPhoto:photo];
}
if (shouldNavigate) {
NSString* URL = [self URLForPhoto:photo];
if (URL) {
TTOpenURLFromView(URL, self.view);
} else {
TTPhotoViewController* controller = [self createPhotoViewController];
controller.centerPhoto = photo;
[self.navigationController pushViewController:controller animated:YES];
}
}
}
In the else statement, I've found this calls the creation of the photoviewcontroller. By recalling this method (?) in the actual body of my own code and changing the body in the else statement I was able to add a custom detail view. Further down the definition of the ttthumbsnailviewcontroller, you can find that the creatPhotoViewController calls for an initiation of the PhotoViewController so calling that method(?) in the body of the code and initializing another view also works.
If someone can explain whether or not this is a good method of doing this (I have a feeling that is not), it would be appreciated. Also why does putting the method in the body of the code override the call there.

Callback for UITableView animations

Is there a delegate protocol that includes a callback for the end of animated changes to UITableView? Particularly reloadSection/Rows?
Actually, there DOES seem to be a way to do this (though not nearly as straightforward as it ought to be!).
While there's no direct access to some kind of callback, it appears that UITableView animations take place within a CAAnimation. Therefore, simply accessing the completionBlock of the CAAnimation seems to give you what you want.
Here's how I got 2 animations to chain in my UITableView subclass:
#pragma mark - Section expanding/collapsing
- (void)toggleSection:(NSInteger)index {
int expandedSection = [self ExpandedSection];
if (expandedSection != NO_EXPANDED_SECTIONS_INDEX) {
[self beginUpdates];
[self collapseSection:#(expandedSection)];
[CATransaction setCompletionBlock:^{
if (expandedSection != index) {
[self beginUpdates];
[self expandSection:#(index)];
[self endUpdates];
}
}];
[self endUpdates];
} else {
[self expandSection:#(index)];
}
}
The code in the collapseSection: and expandingSection: methods simply adds/removes sub-sections that need to be collapsed or expanded. The key point here, though, is that, when using this code, I am finally able to collapse one sub-section THEN expand the next sub-section. Before, both animations were occurring concurrently, which was visually unappealing.
I hope this helps you! I struggled through this for a long time, banging my head against the wall until I found this.
This is written against iOS 6.0. I also really hope that some future version makes this workaround/hack obsolete!
It's not the answer you want to hear but no* :(
The table view's delegate will respond to these methods only.
However, (it's a bit of a hack but) animations are about 0.35 seconds long do you could just call performSelector:withObject:afterdelay: . . .
*at least not as far as I know . . .

iPhone SDK - opening links in a UITextView in a web view

I have specified dataDetectorTypes on a UITextView so that URLs open in Safari when touched.
Is it possible to intercept this behaviour so I load the URL in a UIWebView instead? Or would I have write my own URL detector code to re-route this?
You would have to do the URL detection yourself and manually load the UIWebView when the URL is tapped.
Everything needs to be custom-done because Apple sends all http:// and https:// URL openings to Safari.
The answer above that works best is the replacement of method implementation for [UIApplication openURL:]
Another way to achieve that, without using runtime.h is to subclass UIApplication. Then, override the openURL: selector. With this approach, you can call [super openURL:] from your subclass for URLs you want the default handling for. It also seems a little cleaner to me since you don't need to mess with the internal method implementations of the classes.
If you choose this approach, though, there are 2 other important steps. In the main.m file you need to change the 3rd argument to the UIApplicationMain function call so that it matches the name of your subclass:
int retVal = UIApplicationMain(argc, argv, #"MyApplicationSubclass", nil);
Also, you should probably change the class of the File's Owner in your MainWindow.xib file from UIApplication to your subclass.
I did everyone a favor and answered your question with a blog post and demo app.
http://52apps.net/post/879106231/method-swizzling-uitextview-and-safari
http://github.com/marksands/UITextViewLinkOptions
To expand on tt.Kilew's post, you create the category, but call your method something else such as customOpenURL. When you want to go back to Safari you do something called Method Swizzling. It looks like this:
#import <objc/runtime.h>
..
Method customOpenUrl = class_getInstanceMethod([UIApplication class], #selector(customOpenURL:));
Method openUrl = class_getInstanceMethod([UIApplication class], #selector(openURL:));
method_exchangeImplementations(customOpenUrl, openUrl);
Just call this method to swap the openURL implementation with your customOpenURL implementation when you do and don't want to use Safari.
Check out the demo app for more detail. Hope this helps! :)
Edit
If you don't want to risk your app getting rejected, you might want to check out a custom UITextView I developed to better suit the situation: https://github.com/marksands/MSTextView
Another Answer :) That works fine for me is to re-implement UIApplication openURL:(NSURL *) url
#interface UIApplication (Private)
- (BOOL)openURL:(NSURL*)url;
#end
#implementation UIApplication (Private)
- (BOOL)openURL:(NSURL*)url {
// all viewcontrollers should change currentViewController to self
if ([MyWatcher currentViewController]) {
// Do anything you want
[[MyWatcher handleURL:url withController:[MyWatcher currentViewController]];
return YES;
}
return NO;
}
#end
... Some view controller
- (void)viewDidLoad {
[super viewDidLoad];
[MyWatcher setCurrentController:self];
}
Swift version:
Your standard UITextView setup should look something like this, don't forget the delegate and dataDetectorTypes.
var textView = UITextView(x: 10, y: 10, width: CardWidth - 20, height: placeholderHeight) //This is my custom initializer
textView.text = "dsfadsaf www.google.com"
textView.selectable = true
textView.dataDetectorTypes = UIDataDetectorTypes.Link
textView.delegate = self
addSubview(textView)
After your class ends add this piece:
Note that you need https://github.com/TransitApp/SVWebViewController this library, which is the best one out there as far as I know.
class myVC: UIViewController {
//viewdidload and other stuff here
}
extension MainCard: UITextViewDelegate {
func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
//Do your stuff over here
var webViewController = SVModalWebViewController(URL: URL)
view.presentViewController(webViewController, animated: true, completion: nil)
return false
}
}
You can try implementing application:handleOpenURL: in your Application Delegate.
This method should get called whenever a url gets opened. Here you should be able to make the URL open in your webview.