UIWebView is not zooming more than x2 - objective-c

There are a lot of questions with zooming in UIWebView, but I could not find an answer to mine.
I've got ViewController with WebView. I load an SVG-file into this WebView, and I want to zoom in by tapping on it. Here is the code:
#interface ViewController : UIViewController <UIGestureRecognizerDelegate>
{
IBOutlet MyWebView *webView;
UITapGestureRecognizer *doubleTapRecognizer;
CGFloat k;
}
...
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:#"map" withExtension:#"svg"]]];
webView.scalesPageToFit = YES;
webView.scrollView.maximumZoomScale = 512;
webView.scrollView.minimumZoomScale = 0.1;
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTap:)];
doubleTapRecognizer.delegate = self;
doubleTapRecognizer.enabled = YES;
[webView addGestureRecognizer:doubleTapRecognizer];
k = 1;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (void)doubleTap:(UITapGestureRecognizer*)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
k = 10;
// No one does not work correctly
[webView.scrollView setZoomScale:k animated:YES];
// [webView setContentScaleFactor:k];
// [webView.scrollView setContentScaleFactor:k];
// webView.scrollView.zoomScale = k;
// [webView.scrollView zoomToRect:CGRectMake(webView.bounds.origin.x, webView.bounds.origin.y, webView.bounds.size.width/k, webView.bounds.size.height/k) animated:YES];
}
}
Now WebView is zooming in by the tap, but only x2 and no more. What is wrong and how could I zoom it x10?

Have you tried pinching ?
It's not because you've set the max zoom to be 512 or 10 that tapping twice will zoom to that level, what it means is tapping onto a small element in the web view, this will zoom in to fill the available space of the web view, regardless of the zoom value, up until the max zoom value.
So something appearing to occupy half the screen will make the zoom be x2, smaller would be more.

Related

UIScrollview with paging vertically working but user not landing to the next page

I am using the following code to scroll to the next page of a UITableView that has a scrollview. Scrolling works but the page the user lands to depends on the power he scrolls with his finger. Whatever the force of scrolling is it needs to land to the next page. How can i achieve this? Any help appreciated...
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint
*)targetContentOffset {
self.tableView.rowHeight = [UIScreen mainScreen].bounds.size.height-self.navigationController.navigationBar.frame.size.height-(self.appDelegate.heightOfEightFeedWalletTopConstraint*2)+12;
int tomove = ((int)targetContentOffset->y % (int)self.tableView.rowHeight);
if(tomove < self.tableView.rowHeight/6 || velocity.y < 0){
targetContentOffset->y -= tomove;
}
else{
targetContentOffset->y += (self.tableView.rowHeight-tomove);
}
}
You don't need any of that code...
All you need to do is enable paging on the scroll view.
This will
create a scroll view with 20-points on all 4 sides
add a vertical stack view
add 10 labels as arranged subviews (each label will be the height of the scroll view frame)
Scroll view has paging enabled:
#import <UIKit/UIKit.h>
#interface PageScrollViewController : UIViewController
{
UIScrollView *scrollView;
}
#end
#implementation PageScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
scrollView = [UIScrollView new];
[scrollView setPagingEnabled:YES];
UIStackView *stack = [UIStackView new];
stack.axis = UILayoutConstraintAxisVertical;
stack.distribution = UIStackViewDistributionFillEqually;
stack.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:stack];
[self.view addSubview:scrollView];
// respect safeArea
UILayoutGuide *g = self.view.safeAreaLayoutGuide;
UILayoutGuide *cg = scrollView.contentLayoutGuide;
[NSLayoutConstraint activateConstraints:#[
[scrollView.topAnchor constraintEqualToAnchor:g.topAnchor constant:20.0],
[scrollView.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:20.0],
[scrollView.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:-20.0],
[scrollView.bottomAnchor constraintEqualToAnchor:g.bottomAnchor constant:-20.0],
[stack.topAnchor constraintEqualToAnchor:cg.topAnchor constant:0.0],
[stack.leadingAnchor constraintEqualToAnchor:cg.leadingAnchor constant:0.0],
[stack.trailingAnchor constraintEqualToAnchor:cg.trailingAnchor constant:0.0],
[stack.bottomAnchor constraintEqualToAnchor:cg.bottomAnchor constant:0.0],
[stack.widthAnchor constraintEqualToAnchor:scrollView.frameLayoutGuide.widthAnchor],
]];
// now let's add some views to the stack view
// making each one the same height as the scroll view frame
NSArray <UIColor *> *colors = #[
UIColor.yellowColor,
UIColor.greenColor,
UIColor.cyanColor,
UIColor.orangeColor,
];
for (int i = 0; i < 10; i++) {
UILabel *v = [UILabel new];
v.textAlignment = NSTextAlignmentCenter;
v.text = [NSString stringWithFormat:#"Label %d", i];
v.backgroundColor = colors[i % colors.count];
[stack addArrangedSubview:v];
[v.heightAnchor constraintEqualToAnchor:scrollView.frameLayoutGuide.heightAnchor].active = YES;
}
}
#end

UITabBarController Subviews Don't Appear Weirdness

I have a curious issue with an iOS 9/ObjC app I am writing using a 4 tab format. The "main" ViewController has several subviews with controls and an image. When the user taps the image a PickerView pops up and it is dismissed with another tap on the image. The other tabbed ViewControllers lead to simple views. It was all put together with IB, except for a single subview on the "main" view where I do some Quartz animation drawing superimposed on the image view there.
If I stay on and interact with just the "main" view, all works exactly as desired. The PickerView appears and disappears as designed. Tabbing to another ViewController, then back to the "main" View leads to the problem: A tap on the image view fails to pop up the Picker. The tap is recognized in the Quartz layer, fires a Notification event which is picked up in the picked up in the main view controller which in turn adds the picker subview, brings it to the front and enables interaction just as when it works properly, but the picker never appears, even though the Debugger shows it successfully added to the subview array.
Even stranger, if I tab off the main to any other tab view, then back to main, the picker now appears again as it should. This sequence is completely repeatable - tab once off the main, and the picker doesn't visualize, tab again and then back to main and the picker works.
Any thoughts on where to start looking? Thanks
Here's some code (but I'm not sure where the problem is)
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.picker = [[UIPickerView alloc] init];
self.picker.datasource = self;
self.picker.delegate = self;
float screenWidth = [UIScreen mainScreen].bounds.size.width;
float pickerWidth = screenWidth * 3 / 4;
// Calculate the starting x coordinate.
float xPoint = screenWidth / 2 - pickerWidth / 2;
[self.Picker setFrame: CGRectMake(xPoint, 50.0f, pickerWidth, 250.0f)];
[self.Picker setClipsToBounds:NO];
[self.Picker setBackgroundColor:[UIColor whiteColor]];
self.Picker.showsSelectionIndicator = YES;
self.Picker.userInteractionEnabled = YES;
.
.
.
UIImage *base = [UIImage imageNamed:#"Base"];
self.baseAView = [[UIImageView alloc] initWithFrame:imFrame];
[self.baseView setImage:base];
[self.view addSubview:_baseView];
self.GView = [[GView alloc] initWithFrame:imFrame];
self.GView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_GView];
}
-(void) viewDidAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSin) name:#"touchP" object:nil];
}
- (void) doSin {
// user hits GView, draw Picker if not in View Hierarchy, if in Hierarch, grab value and resign
NSArray *subViews = [self.view subviews];
__block NSInteger foundIndex = NSNotFound;
[subViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UIPickerView class]]) {
foundIndex = idx;
// stop the enumeration
*stop = YES;
}
}];
if (foundIndex != NSNotFound) {
// Found the UIPickerView in subviews, so grab value & remove from screen
[self.view.subviews[foundIndex] removeFromSuperview];
} else {
// not in hierarchy, so put it up on screen
[self.view addSubview:self.Picker];
[self.view bringSubviewToFront:self.Picker];
self.view.userInteractionEnabled = YES;
}
}

Find where UIimageview frame is being set

I am working on an iPad application in Xcode 5 for a jigsaw puzzle game. I created the layout in storyboard, adding uiimageviews for each of 9 frames that would hold part of the image that was going to be the puzzle, and placed them on the view controller. Someone else has since removed the storyboard from the game and I need to go back and finish up the puzzle without it. The biggest issue I have is that the position of the frames seem to be being set somewhere outside of the view controller.h or .m classes and I can't find it. I am able to change the position of the pieces using image1.center in the touchesmoved method, but not in the viewdidload. I have been trying to track down where this is being set for several hours and am having no luck. Setting any movement of the center in viewdidload,viewwillappear, viewdidappear etc do not change the position as expected.
Can anyone either guide me on a way to change the image1.center after the view loads but without any user input, or else tell me how to trace all changes that are occurring in the view? I don't think seeing my code will help in this question since it is not seemingly changed in the classes that I am looking at, but I will post what works and what doesn't. I am only posting the portions that touch the uiimageview (1-9)
from .h
#implementation PuzzleViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
#interface PuzzleViewController : UIViewController
{
UIImageView *image1;
UIImageView *image2;
UIImageView *image3;
UIImageView *image4;
UIImageView *image5;
UIImageView *image6;
UIImageView *image7;
UIImageView *image8;
UIImageView *image9;
UIImage *puzzlePic;
}
from .m
- (void)viewDidLoad
{
[super viewDidLoad];
//set up buttons to accept user input
image1.userInteractionEnabled = true;
image2.userInteractionEnabled = true;
image3.userInteractionEnabled = true;
image4.userInteractionEnabled = true;
image5.userInteractionEnabled = true;
image6.userInteractionEnabled = true;
image7.userInteractionEnabled = true;
image8.userInteractionEnabled = true;
image9.userInteractionEnabled = true;
// Do any additional setup after loading the view from its nib.
puzzlePic = [LLFileManager getImage:#"star7.gif" folder:#"animation_images"];
[self placeImage];
}
// handle touches within images to move
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if([touch view] == image1){
image1.center = touchLocation;
}
else if([touch view] == image2){
image2.center = touchLocation;
}
else if([touch view] == image3){
image3.center = touchLocation;
}
else if([touch view] == image4){
image4.center = touchLocation;
}
else if([touch view] == image5){
image5.center = touchLocation;
}
else if([touch view] == image6){
image6.center = touchLocation;
}
else if([touch view] == image7){
image7.center = touchLocation;
}
else if([touch view] == image8){
image8.center = touchLocation;
}
else if([touch view] == image9){
image9.center = touchLocation;
}
}
// splits image into 9 equal parts, takes input of a ui image
-(NSArray *)splitImage9:(UIImage *)im{
NSMutableArray *images = [NSMutableArray array];
CGSize imageSize = im.size;
CGFloat xPos = 0.0, yPos = 0.0;
CGFloat width = imageSize.width/3;
CGFloat height = imageSize.height/3;
for (int y = 0; y < 3; y++) {
xPos = 0.0;
for (int x = 0; x < 3; x++) {
CGRect rect = CGRectMake(xPos, yPos, width, height);
CGImageRef cImage = CGImageCreateWithImageInRect([im CGImage], rect);
UIImage *dImage = [[UIImage alloc] initWithCGImage:cImage];
[images addObject:dImage];
xPos += width;
}
yPos += height;
}
return images;
}
// position pieces on board load
-(void)positionPieces{ // test, not working
CGPoint piece = CGPointMake(50,100);
image2.center = piece;
}
// places the cut images into view placeholders
-(void)placeImage{
NSArray *puzzleImage = [self splitImage9: puzzlePic];
[image1 setImage:[puzzleImage objectAtIndex:0]];
[image2 setImage:[puzzleImage objectAtIndex:1]];
[image3 setImage:[puzzleImage objectAtIndex:2]];
[image4 setImage:[puzzleImage objectAtIndex:3]];
[image5 setImage:[puzzleImage objectAtIndex:4]];
[image6 setImage:[puzzleImage objectAtIndex:5]];
[image7 setImage:[puzzleImage objectAtIndex:6]];
[image8 setImage:[puzzleImage objectAtIndex:7]];
[image9 setImage:[puzzleImage objectAtIndex:8]];
}
The biggest issue I have is that the position of the frames seem to be being set somewhere outside of the view controller.h or .m classes and I can't find it.
If the storyboard is gone, and if no code has been added to put the image views into the interface, then the interface should be empty. The reason you are seeing the interface at all must be because you are accidentally running an outdated version of the project.
To solve that problem, clean the project as I explain here:
How to Empty Caches and Clean All Targets Xcode 4
You will then find, when you run the project again, that the interface is now empty and the mystery is solved: the frames have no position, because there are no frames, because there are no image views at all.

UIPageControl doesn't properly react to taps

In my TestApp, I created a class "Carousel" which should let me create a swipe menu with a UIPageControl in an easy way (by simply creating an instance of the class Carousel).
Carousel is a subclass of UIView
At init, it creates an UIView, containing UIScrollView, UIPageControl
I can add further UIViews to the scroll view
I don't know if this is the proper way to do it, but my example worked quite well in my TestApp. Swiping between pages works perfectly and the display of the current page in the UIPageControl is correct.
If there were not one single problem: The UIPageControl sometimes reacts to clicks/taps (I only tested in Simulator so far!), sometimes it doesn't. Let's say most of the time it doesn't. I couldn't find out yet when it does, for me it's just random...
As you can see below, I added
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
to my code. I thought this would do the proper handling of taps? But unfortunately, pageChange doesn't get always called (so the value of the UIPageControl doesn't change every time I click).
I would appreciate any input on this because I couldn't find any solution on this yet.
This is what I have so far:
Carousel.h
#import <UIKit/UIKit.h>
#interface Carousel : UIView {
UIScrollView *scrollView;
UIPageControl *pageControl;
BOOL pageControlBeingUsed;
}
- (void)addView:(UIView *)view;
- (void)setTotalPages:(NSUInteger)pages;
- (void)setCurrentPage:(NSUInteger)current;
- (void)createPageControlAt:(CGRect)cg;
- (void)createScrollViewAt:(CGRect)cg;
#end
Carousel.m
#import "Carousel.h"
#implementation Carousel
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Create a scroll view
scrollView = [[UIScrollView alloc] init];
[self addSubview:scrollView];
scrollView.delegate = (id) self;
// Init Page Control
pageControl = [[UIPageControl alloc] init];
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
[self addSubview:pageControl];
}
return self;
}
- (IBAction)pageChange:(id)sender {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageControl.currentPage;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:TRUE];
NSLog(#"%i", pageControl.currentPage);
}
- (void)addView:(UIView *)view {
[scrollView addSubview:view];
}
- (void)createPageControlAt:(CGRect)cg {
pageControl.frame = cg;
}
- (void)setTotalPages:(NSUInteger)pages {
pageControl.numberOfPages = pages;
}
- (void)setCurrentPage:(NSUInteger)current {
pageControl.currentPage = current;
}
- (void)createScrollViewAt:(CGRect)cg {
[scrollView setPagingEnabled:TRUE];
scrollView.frame = cg;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*pageControl.numberOfPages, scrollView.frame.size.height);
[scrollView setShowsHorizontalScrollIndicator:FALSE];
[scrollView setShowsVerticalScrollIndicator:FALSE];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float frac = scrollView.contentOffset.x / scrollView.frame.size.width;
NSUInteger page = lround(frac);
pageControl.currentPage = page;
}
#end
ViewController.m (somewhere in viewDidLoad)
Carousel *carousel = [[Carousel alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
for (int i=0; i<5; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(i * 320, 0, 320, 420)];
UIColor *color;
if(i%3==0) color = [UIColor blueColor];
else if(i%3==1) color = [UIColor redColor];
else color = [UIColor purpleColor];
view.backgroundColor = color;
[carousel addView:view];
view = nil;
}
[carousel setTotalPages:5];
[carousel setCurrentPage:0];
[carousel createPageControlAt:CGRectMake(0,420,320,40)];
[carousel createScrollViewAt:CGRectMake(0,0,320,420)];
Your code is correct. Most likely the frame of your pageControl is pretty small, so theres not a lot of area to look for touch events. You would need to increase the size of the height of pageControl in order to make sure taps are recognized all of the time.

How do I center a UIImageView within a full-screen UIScrollView?

In my application, I would like to present the user with a full-screen photo viewer much like the one used in the Photos app. This is just for a single photo and as such should be quite simple. I just want the user to be able to view this one photo with the ability to zoom and pan.
I have most of it working. And, if I do not center my UIImageView, everything behaves perfectly. However, I really want the UIImageView to be centered on the screen when the image is sufficiently zoomed out. I do not want it stuck to the top-left corner of the scroll view.
Once I attempt to center this view, my vertical scrollable area appears to be greater than it should be. As such, once I zoom in a little, I am able to scroll about 100 pixels past the top of the image. What am I doing wrong?
#interface MyPhotoViewController : UIViewController <UIScrollViewDelegate>
{
UIImage* photo;
UIImageView *imageView;
}
- (id)initWithPhoto:(UIImage *)aPhoto;
#end
#implementation MyPhotoViewController
- (id)initWithPhoto:(UIImage *)aPhoto
{
if (self = [super init])
{
photo = [aPhoto retain];
// Some 3.0 SDK code here to ensure this view has a full-screen
// layout.
}
return self;
}
- (void)dealloc
{
[photo release];
[imageView release];
[super dealloc];
}
- (void)loadView
{
// Set the main view of this UIViewController to be a UIScrollView.
UIScrollView *scrollView = [[UIScrollView alloc] init];
[self setView:scrollView];
[scrollView release];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Initialize the scroll view.
CGSize photoSize = [photo size];
UIScrollView *scrollView = (UIScrollView *)[self view];
[scrollView setDelegate:self];
[scrollView setBackgroundColor:[UIColor blackColor]];
// Create the image view. We push the origin to (0, -44) to ensure
// that this view displays behind the navigation bar.
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, -44.0,
photoSize.width, photoSize.height)];
[imageView setImage:photo];
[scrollView addSubview:imageView];
// Configure zooming.
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
CGFloat widthRatio = screenSize.width / photoSize.width;
CGFloat heightRatio = screenSize.height / photoSize.height;
CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;
[scrollView setMaximumZoomScale:3.0];
[scrollView setMinimumZoomScale:initialZoom];
[scrollView setZoomScale:initialZoom];
[scrollView setBouncesZoom:YES];
[scrollView setContentSize:CGSizeMake(photoSize.width * initialZoom,
photoSize.height * initialZoom)];
// Center the photo. Again we push the center point up by 44 pixels
// to account for the translucent navigation bar.
CGPoint scrollCenter = [scrollView center];
[imageView setCenter:CGPointMake(scrollCenter.x,
scrollCenter.y - 44.0)];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlackTranslucent];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[[self navigationController] navigationBar] setBarStyle:UIBarStyleDefault];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return imageView;
}
#end
This code should work on most versions of iOS (and has been tested to work on 3.1 upwards).
It's based on the Apple WWDC code for PhotoScroller.
Add the below to your subclass of UIScrollView, and replace tileContainerView with the view containing your image or tiles:
- (void)layoutSubviews {
[super layoutSubviews];
// center the image as it becomes smaller than the size of the screen
CGSize boundsSize = self.bounds.size;
CGRect frameToCenter = tileContainerView.frame;
// center horizontally
if (frameToCenter.size.width < boundsSize.width)
frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
else
frameToCenter.origin.x = 0;
// center vertically
if (frameToCenter.size.height < boundsSize.height)
frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
else
frameToCenter.origin.y = 0;
tileContainerView.frame = frameToCenter;
}
Have you checked out the UIViewAutoresizing options?
(from the documentation)
UIViewAutoresizing
Specifies how a view is automatically resized.
enum {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;
Are you using IB to add the scroll view? Change the autosizing options of the scrollview to the attached image.
I think the reason behind it is because the zoomScale applies to the whole contentSize, regardless of the actual size of the subview inside the scrollView (in your case it's an imageView). The contentSize height seems to be always equal or greater than the height of the scrollView frame, but never smaller. So when applying a zoom to it, the height of the contentSize gets multiplied by the zoomScale factor as well, that's why you're getting an extra 100-something pixels of vertical scroll.
You probably want to set the bounds of the scroll view = bounds of the image view, and then center the scroll view in its containing view. If you place a view inside a scroll view at an offset from the top, you will get that empty space above it.