How to enable pinch zoom on a Mac WebView? - objective-c

I've already searched "everything" about this in Google/Stackoverflow, but I'm still stuck. I have just started developing OSX Apps, so I'm a (almost) complete newbie in Objective-C and Xcode 5 (5.0.2).
All I need is a simple webview to load a webgame from a given URL. This webview must behave just like a very simple Safari browser. My app is already working relatively well. It loads the game OK, and after a lot of struggling I succeeded making it show javascript alerts and confirms.
My task is now make the pinch zoom work. That's my appDelegate.M:
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize myWebView;
// Enables confirms:
- (BOOL)webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
NSInteger result = NSRunInformationalAlertPanel(NSLocalizedString(#"Confirmação", #""), // title
message, // message
NSLocalizedString(#"OK", #""), // default button
NSLocalizedString(#"Cancelar", #""), // alt button
nil);
return NSAlertDefaultReturn == result;
}
// Enables alerts:
- (void)webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:#"OK"];
[alert setMessageText:message];
[alert runModal];
//[alert release];
}
// This was supposed to enable pinch zoom:
- (void)magnifyWithEvent:(NSEvent *)event {
//[resultsField setStringValue:
//[NSString stringWithFormat:#"Magnification value is %f", [event magnification]]];
NSSize newSize;
newSize.height = [[NSScreen mainScreen] frame].size.height * ([event magnification] + 1.0);
newSize.width = [[NSScreen mainScreen] frame].size.width * ([event magnification] + 1.0);
//[self setFrameSize:newSize];
[myWebView setFrameSize:newSize];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
[self.window setContentView:self.myWebView];
[self.window toggleFullScreen:#""];
[myWebView setMainFrameURL:#"http://www.mywebgameurl.com"];
}
#end
Evidently, the code inside - (void)magnifyWithEvent:(NSEvent *)event is not working. The app builds and runs, but nothing happens when I pinch.
In other words, I need to enable the webview to zoom in and out using the trackpad pinch functionality. I've already developed a very similar webview in Android, and all I had to do was add webSettings.setBuiltInZoomControls(true);
webSettings.setSupportZoom(true);. I'm just looking for something like that. Any help is welcome, thanks!!

You just can't, Cocoa webviews doesnt have such control over pinch zoom functionality.

Related

UISwipegesturerecognizer to change ImageView positions on the view controller?

I'm an Objective-C beginner and I've found similar answers on stackoverflow to my question, but even after trying all of the tips/advice I'm still lost.
I have an IBOutlet UIImageView, and I want it to be so that if the user swipes up or down on this Image, then the position of various images on the view controller change.
I'm very green with the UISwipegesturerecognizer coding, so please explain in a way a beginner would understand. Thank you very much in advance!!!
UPDATE:
#Axeva, I used the following code, and there are no caution warnings or errors. However, it isn't executing properly on my simulator. Here is my code :
interface math0 (){
UISwipeGestureRecognizer *swipeUp;}
- (void)viewDidLoad
{
[super viewDidLoad];
swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget: self action: #selector(swipedScreenUp:)];
[swipeUp setNumberOfTouchesRequired: 1];
[swipeUp setDirection: UISwipeGestureRecognizerDirectionUp];
[one addGestureRecognizer: swipeUp];
[swipeUp setEnabled: NO];
}
- (void)swipedScreenUp:(UISwipeGestureRecognizer*)swipeGesture {
// Sampling to see if it works
instructions.text = [NSString stringWithFormat:#"IT WORKED!"];
}
In my .h file, I declared - (void)swipedScreenUp:(UISwipeGestureRecognizer)swipeUp;. On my storyboard, I set my image (named one) so that it was accessibility and user interaction enabled.
I've also tried with [swipeUp setEnabled: YES]; in my viewDidLoad file.
Can you or anyone tell me where the error is? Thank you!!
Try something like this:
#interface YourViewController () {
UISwipeGestureRecognizer *swipeLeftToRightGesture;
}
- (void)viewDidLoad {
[super viewDidLoad];
swipeLeftToRightGesture = [[UISwipeGestureRecognizer alloc] initWithTarget: self action: #selector(swipedScreenRight:)];
[swipeLeftToRightGesture setNumberOfTouchesRequired: 1];
[swipeLeftToRightGesture setDirection: UISwipeGestureRecognizerDirectionRight];
[[self view] addGestureRecognizer: swipeLeftToRightGesture];
}
- (void)swipedScreenRight:(UISwipeGestureRecognizer*)swipeGesture {
// Move your image views as desired
}
You can adjust this basic code to do exactly what you wish. (different swipe directions; etc.)

iOS Simulator Not Recognizing Gestures

I am adding a UISwipeGestureRecognizer and a UITapGestureRecognizer to a view in a view controller's viewDidLoad method.
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addGestureRecognizer:[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(cardSwipe:)]];
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(cardTap:)]];
}
- (void)cardSwipe:(UISwipeGestureRecognizer *)sender {
//get the card. set faceUp to false.
CGPoint location = [sender locationInView:sender.view];
NSIndexPath *cellIndex = [self.cardCollectionView indexPathForItemAtPoint:location];
if(cellIndex){
UICollectionViewCell *cell = [self collectionView:self.cardCollectionView cellForItemAtIndexPath:cellIndex];
if(cell && [cell isKindOfClass:[CardCollectionViewCell class]]){
[[((CardCollectionViewCell *)cell) cardView] handleCardSwipe];
}
}
}
- (void)cardTap:(UITapGestureRecognizer *)sender {
//get the card. set faceUp to false.
CGPoint location = [sender locationInView:sender.view];
NSIndexPath *cellIndex = [self.cardCollectionView indexPathForItemAtPoint:location];
if(cellIndex){
UICollectionViewCell *cell = [self collectionView:self.cardCollectionView cellForItemAtIndexPath:cellIndex];
if(cell && [cell isKindOfClass:[CardCollectionViewCell class]]){
[[((CardCollectionViewCell *)cell) cardView] handleCardSwipe];
}
}
}
In case this is relevant: The view contains a UICollectionView.
The taps and swipes are not getting recognized. Is there something obvious that I am missing?
Thanks.
Restarting the simulator worked for me.
Turns out the view was not responding to any gestures - scrolling, taps on buttons or the swipe actions. I deleted generated folders from ~/Library/Application Support/iPhone Simulator / 6.1/Applications and ~/Library/Developer/Xcode/DerivedData, reset the simulator settings (from iOS Simulator > Reset Contents and Settings), did a clean in xcode (Product > Clean) and ran the app again. The gestures are now recognized. I am not sure which of the above fixed the problem...it is possible that simply resetting the simulator's contents and settings would have been enough.
You just need to select Show Device Bezels:
Goto simulator > Window > Enable Show Device Bezels
Enjoy your swipe to back gesture.
add this method to your viewcontroller so your UICollectionView doesn't block other gestures
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return true;
}
In case you have already enabled Device Bezels from Window panel but still can't use swipe to go back gesture. Please refer to this answer.
All you need to do is restart the simulator and try to swipe from the real edge of the simulator.
First you Need To Add UITapGestureRecognizer Delegate method To .h
#interface ViewController : UIViewController<UIGestureRecognizerDelegate>
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapImgView:)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.delegate = self;
- (void)doubleTapImgView:(UITapGestureRecognizer *)gesture
{
//Do What you want Here
}

Reading touch events in a QLPreviewController

I've got a QuickLook view that I view some of my app's documents in. It works fine, but I'm having my share of trouble closing the view again. How do I create a touch event / gesture recognizer for which I can detect when the user wants to close the view?
I tried the following, but no events seem to trigger when I test it.
/------------------------ [ TouchPreviewController.h ]---------------------------
#import <Quicklook/Quicklook.h>
#interface TouchPreviewController : QLPreviewController
#end
//------------------------ [ TouchPreviewController.m ]---------------------------
#import "TouchPreviewController.h"
#implementation TouchPreviewController
- (id)init:(CGRect)aRect {
if (self = [super init]) {
// We set it here directly for convenience
// As by default for a UIImageView it is set to NO
UITapGestureRecognizer *singleFingerDTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleDoubleTap:)];
singleFingerDTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:singleFingerDTap];
[self.view setUserInteractionEnabled:YES];
[self.view setMultipleTouchEnabled:YES];
//[singleFingerDTap release];
}
return self;
}
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *) sender {
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[UIView beginAnimations:nil context:NULL];
sender.view.center = tapPoint;
[UIView commitAnimations];
NSLog(#"TouchPreviewController tap!" ) ;
}
// I also tried adding this
- (BOOL)gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*) otherGestureRecognizer {
return YES;
}
#end
Edit: For clarification, this is how I instantiate the controller:
documents = [[NSArray alloc] initWithObjects: filename , nil ] ;
preview = [[TouchPreviewController alloc] init];
preview.dataSource = self;
preview.delegate = self;
//set the frame from the parent view
CGFloat w= backgroundViewHolder.frame.size.width;
CGFloat h= backgroundViewHolder.frame.size.height;
preview.view.frame = CGRectMake(0, 0,w, h);
//refresh the preview controller
[preview reloadData];
[[preview view] setNeedsLayout];
[[preview view] setNeedsDisplay];
[preview refreshCurrentPreviewItem];
//add it
[quickLookView addSubview:preview.view];
Also, I've defined the callback methods as this:
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller
{
return [documents count];
}
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index
{
return [NSURL fileURLWithPath:[documents objectAtIndex:index]];
}
Edit2: One thing i noticed. If I try making swiping gestures, I get the following message. This could shed some light on what is wrong/missing?
Ignoring call to [UIPanGestureRecognizer setTranslation:inView:] since
gesture recognizer is not active.
I think your example code is incomplete. It isn't clear how you are instantiating the TouchPreviewController (storyboard, nib file or loadView.)
I have never used the class so I could be way out in left field.
If you've already instantiated a UITapGestureRecognizer in the parent viewController, it is absorbing the tap events and they aren't passed on to your TouchPreviewController.
I would implement the view hierarchy differently by attaching the UITapGestureRecognizer to the parent viewController and handle presentation and unloading of the QLPreviewController there.
I think you might not have to subclass QLPreviewController by instantiating the viewController from a nib file.
When your parent viewController's UITapGestureRecognizer got an event you would either push the QLPreviewController on the navigation stack or pop it off the navigation stack when done.
Hope this is of some help.

Can't beginReceivingRemoteControlEvents in iOS

In my app i want let user to control audio playback in background. I set backGround modes in .plist, and in plays in bg just like i wanted.But i can't get any response from touching the control buttons.
I set the AudioSession like this
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance]setActive:YES error:nil];
Then, in viewContoller where my player is placed i beginReceivingRemoteControlEvents like this
if ([[UIApplication sharedApplication] respondsToSelector:#selector(beginReceivingRemoteControlEvents)]){
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
[self becomeFirstResponder];
NSLog(#"Responds!");
}
And it prints Responds!
But the problem is that this method is never called
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
NSLog(#"Where is my event?");
if(event.type == UIEventTypeRemoteControl)
{
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
NSLog(#"Pause");
[self playWords:playButton];
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(#"Next");
[self next];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
NSLog(#"Prev");
[self prev];
break;
}
}
I even tried to write a category on UIApplication to let it become the first responder, but it doesn't help
#implementation UIApplication (RemoteEvents)
-(BOOL)canBecomeFirstResponder
{
return YES;
}
#end
Why can this happen?
SOLUTION
Here's what solved my problem Entering background on iOS4 to play audio
I have done the same work in my project, it's working fine. Please follow this, perhaps it will help you. Change the event name etc. In my code _audio is the object of AVAudioPlayer.
- (void)viewDidLoad {
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &setCategoryErr];
[[AVAudioSession sharedInstance] setActive: YES error: &activationErr];
}
- (void)viewWillAppear {
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
[_audio play];
break;
case UIEventSubtypeRemoteControlPause:
[_audio pause];
break;
default:
break;
}
}
There is a newer mechanism for listening to remote control events. For example, to execute a block when the headphone play/pause button is pressed:
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
NSLog(#"toggle button pressed");
return MPRemoteCommandHandlerStatusSuccess;
}];
or, if you prefer to use a method instead of a block:
[commandCenter.togglePlayPauseCommand addTarget:self action:#selector(toggleButtonAction)];
To stop:
[commandCenter.togglePlayPauseCommand removeTarget:self];
or:
[commandCenter.togglePlayPauseCommand removeTarget:self action:#selector(toggleButtonAction)];
You'll need to add this to the includes area of your file:
#import MediaPlayer;
For it to work in the background, you must have the background audio mode added to your app's capabilities.
Review this sample code from Apple for an example of usage. Likely your primary view controller is not actually becoming the first responder. An alternative usage is to place this code in your Application Delegate (which will always be the first responder) and respond to these events before they have had a chance to propagate, and potentially be consumed by other responders.
You have
respondsToSelector:#selector(beginReceivingRemoteControlEvents)]){
but in the method signature you have
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
The place where you are registering the signature is wrong. Just replace it by
respondsToSelector:#selector(beginReceivingRemoteControlEvents:)]){
You are missing the : which is making the app send the even to a non existing method I am assuming. I am surprised that your app is not crashing.

iAds Loading Throttled After Re-Launching App From Background (Also Happens In iAdSuite)

I am trying to implement the NavigationBanner iAdSuite example into my project so that a I can share a single AdBannerView instance across multiple view controllers, but I keep getting the following error:
Error Domain=ADErrorDomain Code=2 "The operation couldn’t be completed. Loading throttled
I have copied the relevant code exactly from the current iAdSuite into my own app and am getting this error. In fact, this error is repeatable in Apple's own iAdSuite example for NavigationBanner (which is the example I am trying to implement). The error can be seen by adding:
NSLog (#"%#",error);
to:
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
To replicate the problem in iAdSuite do the following:
Turn your device Airplane mode to On
Launch iAdSuite NavigationBanner from Xcode. This generates an error right away "ADErrorDomain error 1".
Exit the app by pressing the Home button on your device, then turn Airplane mode Off.
Re-launch NavigationBanner by tapping the icon, and the error appears.
This is a problem for my application because I want to hide the iAd if there is no connectivity, and then have it re-appear once connectivity resumes. If the app receives the throttling error, then there will be a long delay before it can receive another ad.
How can the throttling error be avoided? I was thinking that the bannerView needs to be removed and then re-added, but could not figure out how to do this correctly.
One last thing to note is that the current iAdSuite uses ARC while my application does not. Even so, the error occurs with both my app and iAdSuite.
Try detecting the network status with the "Reachability" project code by Apple. There is an ARC compatible version on Github. (https://github.com/tonymillion/Reachability) Once you have Reachability.h imported in your header file, you can try the code below. Reachability will detect if any sort of connection is available and if not, then the iAd will be moved off screen. Hope this helps!
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
Reachability *reachability = [Reachability reachabilityForInternetConnection];
[reachability startNotifier];
NetworkStatus status = [reachability currentReachabilityStatus];
if(status == NotReachable)
{
// No internet connection. We need to move the iAd off screen.
NSLog(#"No network connection. iAd will hide.");
banner.frame = CGRectOffset(banner.frame, 320, 0);
}
if(status == ReachableViaWifi)
{
banner.frame = CGRectOffset(banner.frame, your position here);
}
if(status == ReachableViaWWAN)
{
banner.frame = CGRectOffset(banner.frame, your position here);
}
}
/*Implement the iAd in app delegate and use the applicationDidBecomeActive method.Here I use #import "Reachability.h" class downloaded from Github Here is the code.*/
// AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate,ADBannerViewDelegate>
{
BOOL iAdLauchFlag;
ADBannerView *bannerView;
UILabel *notifier ;
UIView *iAdview;
}
// AppDelegate.m
#import "AppDelegate.h"
#import "Reachability.h"
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
LauchFlag=NO;
notifier=[[UILabel alloc]init];
notifier=[[UILabel alloc]initWithFrame:CGRectMake(0.0f, 40.0f, bounds.size.height, 30)];
iAdview =[[UIView alloc]initWithFrame:CGRectMake(0.0f,bounds.size.width,bounds.size.height, 30)];
}
-(void) applicationDidBecomeActive: (UIApplication *) application
{
NSLog(#"applicationDidBecomeActive");
if ( [self connectedToNetwork] )
{
if(!LauchFlag)
{
CGRect bounds=[[UIScreen mainScreen] bounds];
NSLog(#"allocated banner view");
bannerView = [[ADBannerView alloc]
initWithFrame:CGRectMake(0.0f, 30.0f, bounds.size.height, 30)];
[notifier setText:#" Connecting to iAd service......."];
[iAdview addSubview:notifier];
}
bannerView.delegate = self;
}
else
{
if(LauchFlag)
{
[bannerView removeFromSuperview];
[bannerView release];
LauchFlag=NO;
}
[notifier setText:#" iAd failed to launch due to internet connection problem "];
[iAdview addSubview:notifier];
}
}
-(BOOL)bannerViewActionShouldBegin:
(ADBannerView *)banner
willLeaveApplication:(BOOL)willLeave{
return YES;
}
- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(#"bannerView:(ADBannerView *)banner didFailToReceiveAdWithError");
if ([self connectedToNetwork]) {
[notifier setText:#" Launching iAd ............"];
NSLog(#"Reachable");
}
else {
[notifier setText:#"error: iAd failed to launch due internet connection problem "];
NSLog(#"Not Reachable");
}
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
NSLog(#"bannerViewDidLoadAd");
[notifier removeFromSuperview];
[iAdview addSubview:bannerView];
LauchFlag=YES;
}
- (BOOL) connectedToNetwork
{
Reachability *r = [Reachability reachabilityWithHostName:#"www.google.com"];
NetworkStatus internetStatus = [r currentReachabilityStatus];
BOOL internet;
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN)) {
internet = NO;
} else {
internet = YES;
}
return internet;
}
// viewcontroller1
#import "AppDelegate.h"
- (void)viewDidLoad
{
AppDelegate *appdelegate=(AppDelegate *)[[UIApplication sharedApplication] delegate];
[[self view] addSubview:appdelegate.iAdview];
}
//viewcontroller2
#import "AppDelegate.h"
- (void)viewDidLoad
{
AppDelegate *appdelegate=(AppDelegate *)[[UIApplication sharedApplication] delegate];
[[self view] addSubview:appdelegate.iAdview];
}