displaying Wiki mobile page in UIWebView within UIPopoverController - uiwebview

I try to open wiki mobile version webpage by a UIWebView within a UIPopoverController. the problem is, not matter how I set my contentSizeForViewInPopover, or just UIWebView frame, or simply set UIWebView.scalesPageToFit = YES. the Wiki mobile version page content size seem to larger than my UIWebView. But if I use it on iPhone, there's no such problem. here's my code for popover controller:
//create a UIWebView UIViewController first
WikiViewController *addView = [[WikiViewController alloc] init];
addView.contentSizeForViewInPopover = CGSizeMake(320.0, 480.0f);
//then create my UIPopoverController
popover = [[UIPopoverController alloc] initWithContentViewController:addView];
popover.delegate = self;
[addView release];
//then get the popover rect
CGPoint pointforPop = [self.mapView convertCoordinate:selectAnnotationCord
toPointToView:self.mapView];
CGRect askRect = CGRectMake((int)pointforPop.x, (int)pointforPop.y+10, 1.0, 1.0);
[popover presentPopoverFromRect:askRect
inView:self.mapView
permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
[self.mapView deselectAnnotation:annotation animated:YES];
and this is my code on creating UIWebView:
- (void)viewDidLoad
{
wikiWebView = [[UIWebView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];
wikiWebView.scalesPageToFit = YES;
//or No, doesn't matter, it all get larger than this
wikiWebView.delegate = self;
self.view = wikiWebView;
}
all code seem to be typical...
I wonder if anyone can shed me some light, thank you so much.

This is an enhanced version of auco answer, where if the viewport meta tag is not present it will be added:
- (void)webViewDidFinishLoad:(UIWebView*)webView
{
int webviewWidth = (NSUInteger)webView.frame.size.width;
if (!webView.loading) {
NSString *jsCmd = [NSString stringWithFormat:#"try {var viewport = document.querySelector('meta[name=viewport]');if (viewport != null) {viewport.setAttribute('content','width=%ipx, initial-scale=1.0, user-scalable=1');} else {var viewPortTag=document.createElement('meta');viewPortTag.id='viewport';viewPortTag.name = 'viewport';viewPortTag.content = 'width=%ipx, initial-scale=1.0, user-scalable=1';document.getElementsByTagName('head')[0].appendChild(viewPortTag);}} catch (e) {/*alert(e);*/}", webviewWidth, webviewWidth];
[webView stringByEvaluatingJavaScriptFromString:jsCmd];
}
}
Here is the Javascript pretty formatted code we are injecting in the WebView with a width of 320px
try {
var viewport = document.querySelector('meta[name=viewport]');
if (viewport != null) {
viewport.setAttribute('content',
'width=320px, initial-scale=1.0, user-scalable=1');
} else {
var viewPortTag = document.createElement('meta');
viewPortTag.id = 'viewport';
viewPortTag.name = 'viewport';
viewPortTag.content = 'width=320px,initial-scale=1.0, user-scalable=1';
document.getElementsByTagName('head')[0].appendChild(viewPortTag);
}
} catch (e) {
/*alert(e);*/
}
you can remove the try/catch if you want.

oh, i found in another QA that sometimes if html got a line "width=device-width", and you load a webview from popover controller, this popover controller will automatically send out device-width, not the view width you specified, and make your view ugly and funky. in that post it is a jQuery issue, and it solved with a jQuery way. In my problem, it is just a html issue in wiki mobile version. so I try another way, but similar.
I simple add a code in webViewdidload delegate method, first get URL html into a NSString, then use NSString instance method to search for "device-width" in loaded html, and replace it with my view width to make it a new NSString, then load this page with this new NSString. that's it.
- (void) webViewDidFinishLoad:(UIWebView *)webView
{
if (!alreadyReload)
{
NSString *webHTML = [NSString stringWithContentsOfURL:webView.request.URL encoding:NSUTF8StringEncoding error:NULL];
NSRange range = [webHTML rangeOfString:#"device-width"];
if ((range.location!=NSNotFound)&&(range.length != 0))
{
webHTML = [webHTML stringByReplacingOccurrencesOfString:#"device-width" withString:#"whatever width you need" options:0 range:range];
[webView loadHTMLString:webHTML baseURL:wikiWebView.request.URL];
alreadyReload = YES;
}
}
}
something like this.
by the way, since I only use this on wiki mobile version, the html is simple and this kind of compare and replace is pretty easy. if you wanna use it in a more general case, you might use other way.

It would be much more efficient to manipulate the device-width via JavaScript rather than altering the html after it has fully loaded and then reloading the full page with modified html again.
This should work (and also consider if it's even necessary to change the viewport width):
- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
if(aWebView.frame.size.width < aWebView.window.frame.size.width) {
// width=device-width results in a wrong viewport dimension for webpages displayed in a popover
NSString *jsCmd = #"var viewport = document.querySelector('meta[name=viewport]');";
jsCmd = [jsCmd stringByAppendingFormat:#"viewport.setAttribute('content', 'width=%i, initial-scale=1.0, user-scalable=1');", (NSUInteger)aWebView.frame.size.width];
[aWebView stringByEvaluatingJavaScriptFromString:jsCmd];
}
// stop network indicator
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

Related

UITextView to Mimic UITextField [for basic round corner text field]

I want to have UITextField with multiple lines, after a quick google on this issue I found that I should use TextView so I did switch my code to use UITextView when I want multiple lines. My View still have other one line textField that I want to keep.
To make my TextView looks like TextField, I had to add code to set border and radius, but they look a little bit different on iOS7. Does anyone know:
what is the color for the UITextField border? when both enabled and disabled so I can sent my textview to match it.
what is radius of the corner of TextField.
what is the background color for UITextField when it is disabled[attached picture shows the text field has lighter shade of grey when it is disabled]? so i can set my text view to the same color when i disable user interaction.
If there is away to keep using textfield for multiline text, I am all ears and i switch to use it.
Best Regards,
I use this:
textView.layer.borderColor = [[UIColor colorWithRed:215.0 / 255.0 green:215.0 / 255.0 blue:215.0 / 255.0 alpha:1] CGColor];
textView.layer.borderWidth = 0.6f;
textView.layer.cornerRadius = 6.0f;
little differences in the params make it looks more like UITextField(I hope).
I use this:
#import <QuartzCore/QuartzCore.h>
-(void) textViewLikeTextField:(UITextView*)textView
{
[textView.layer setBorderColor:[[UIColor colorWithRed:212.0/255.0
green:212.0/255.0
blue:212.0/255.0
alpha:1] CGColor]];
[textView.layer setBorderWidth:1.0f];
[textView.layer setCornerRadius:7.0f];
[textView.layer setMasksToBounds:YES];
}
and get a good resut.
I have a small subclass of UITextView that gives in iOS 7 the same look as the UITextField
The interface is empty:
#interface MyTextView : UITextView
#end
The implementation overwrites the initialization and the setter of the 'editable' property:
#implementation MyTextView
//================================================================================
- (id) initWithFrame: (CGRect) frame
{
self = [super initWithFrame:frame];
if (self)
{
[self commonInit];
}
return self;
}
//================================================================================
- (id) initWithCoder: (NSCoder *) aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self commonInit];
}
return self;
}
//================================================================================
- (void) commonInit
{
self.layer.borderWidth = 1.0f;
self.layer.borderColor = [[UIColor colorWithRed:232.0/255.0
green:232.0/255.0 blue:232.0/255.0 alpha:1] CGColor];
self.layer.cornerRadius = 6;
}
//================================================================================
- (void) setEditable: (BOOL) editable
{
[super setEditable:editable];
if (editable)
{
self.backgroundColor = [UIColor whiteColor];
}
else
{
self.backgroundColor = [UIColor colorWithRed:250.0/255.0
green:250.0/255.0 blue:250.0/255.0 alpha:1];
}
}
//================================================================================
#end
This is the closest I got with enabled UITextView
[yourTextView.layer setBorderColor:[[[UIColor lightGrayColor] colorWithAlphaComponent:0.2] CGColor]];
[yourTextView.layer setBorderWidth:2.0];
yourTextView.layer.cornerRadius = 5;
yourTextView.clipsToBounds = YES;
yourTextView.textColor = [UIColor lightGrayColor];

How to use NSCache with CoreData

I have developed a project, where a user draws a image on a canvas, I store it in the file using CoreData, I have one-to-many relationship called folder-to-files. So here all are images. I retrive the images from files , resize according to my table cell height and show it on a table. Once it is shown, I want to cache the images.
I also have some labels on the folder cell, which give me some info regarding my files, which I update on fly.
I also swipe the cells to mark it complete and move it to the bottom the cell.
I also show same file images in different Views depending on how user queries it.
I want to know the best method for this, I read through the web, their are many methods, GCD, NSOperationQueues and many more.
Which method will be best suited for me.
I want to show some code
- (UITableViewCell *)tableView:(FMMoveTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
FolderCell *tableCell = (FolderCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (tableCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"FolderCell" owner:self options:nil];
tableCell = [nib objectAtIndex:0];
}
NSMutableArray *categoryArray = [[self.controller fetchedObjects]mutableCopy];
Folder *category = [categoryArray objectAtIndex:[indexPath row]];
[tableCell configureCellWithNote:category]; //This function is written in my FolderCell.m function
}
return tableCell;
}
-(void)configureCellWithNote:(Folder *)category
{
self.category = category;
UIImage *image1 = [UIImage imageWithData:category.noteImage];
CGSize newSize;
if(image1.size.width == 620 && image1.size.height == 200)
{
newSize = CGSizeMake(300, 97);
}
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image1 drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.notesImage.image = newImage;
}
So what is happening here is that configureCellWithNote is taking lot of time, because it is resizing images. Please help me out in deciding how can this performance issue be solved.
Regards
Rajit
If you simply want to shuffle the resize operation to a background thread, you could do something like this:
- (void)configureCellWithNote:(Folder *)category
{
self.category = category;
UIImage *image1 = [UIImage imageWithData:category.noteImage];
CGSize newSize;
if(image1.size.width == 620 && image1.size.height == 200)
{
newSize = CGSizeMake(300, 97);
}
dispatch_async(dispatch_get_global_queue(0,0), ^{
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image1 drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
self.notesImage.image = newImage;
});
});
}
If you want to cache the results, then the trick will be to come up with a good cache key. Unfortunately, it's hard to tell from what you've posted what would make a good cache key. Certainly it will need to include the size, but it'll also need to include something that ties it back to the category. I suppose if nothing else you could use the NSManagedObjectID for the category, but I think that'll be specific to each managed object context you have. Assuming there was a property on Folder called uniqueName a caching implementation might look like this:
- (UIImage*)imageForCategory: (Folder*)category atSize: (CGSize)size
{
// A shared (i.e. global, but scoped to this function) cache
static NSCache* imageCache = nil;
// The following initializes the cache once, and only once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
imageCache = [[NSCache alloc] init];
});
// Generate a cache key sufficient to uniquely identify the image we're looking for
NSString* cacheKey = [NSString stringWithFormat: #"%#|%#", category.uniqueName, NSStringFromSize((NSSize)size)];
// Try fetching any existing image for that key from the cache.
UIImage* img = [imageCache objectForKey: cacheKey];
// If we don't find a pre-existing one, create one
if (!img)
{
// Your original code for creating a resized image...
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
UIImage* image1 = [UIImage imageWithData:category.noteImage];
[image1 drawInRect:CGRectMake(0,0,size.width,size.height)];
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Now add the newly-created image to the cache
[imageCache setObject: img forKey: cacheKey];
}
// Return the image
return img;
}

Change Navigation Bar Tint Color while presenting new view controller modally in iOS 4.x

In my view controller's I have following code
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"%s",__PRETTY_FUNCTION__);
self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:201.0/255.0 green:201.0/255.0 blue:201.0/255.0 alpha:1.0];
}
On this view when I click a button , I am presenting MFMailComposeViewController modally. When I present MFMailComposeViewController modally, navigation bar same color as set in viewWillAppear. I want default blue color as Apple told that don't customize this view. Here is my code which I have tried
Code 1:
-(void)sendEmailWithWithRecipients:(NSArray*)recipients andData:(NSData*)data {
if ([MFMailComposeViewController canSendMail]){
MFMailComposeViewController *mailComposeViewController = [[MFMailComposeViewController alloc] init];
mailComposeViewController.mailComposeDelegate = self;
self.navigationController.navigationBar.tintColor = nil;
[self presentModalViewController:mailComposeViewController animated:YES];
[mailComposeViewController release];
}
else {
[self showAlertWithTitle:#"Alert" andMessage:#"Can not send email. Please check your email settings."];
}
}
But above code is not working for tint color
Code 2: Set tint color in viewWillDisappear
-(void)viewWillDisappear:(BOOL)animated {
NSLog(#"%s",__PRETTY_FUNCTION__);
self.navigationController.navigationBar.tintColor = nil;
[super viewWillDisappear:animated];
}
Code 2 also not working.
But this is working fine in iOS5 without setting tintColor = nil while presenting MFMailComposeViewController modally & in viewWillDisappear. Its not working iOS 4.x
Anyone have solution for this? Thanks.
You can customize the navigationbar's tint color by accessing yourController's(ViewController which you are presetnting) topViewController.
Ex :
[self presentModalViewController:yourController animated:YES];
yourController.topViewController.navigationController.navigationBar.tintColor =
[UIColor (Your Color)];
Try setting the navigation bar style rather than the tint color. Tint color and bar style have slightly different side effects, so while I don't completely understand your needs, it might be exactly what you're looking for.
[self.navigationController.navigationBar setBarStyle:UIBarStyleDefault];
or
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
And make sure you do this within the context of the modal view controller, not the previous view controller.
Instead of nil try this [UIColor ClearColor];

How to keep a drag able object inside the frame of a view

I have a button that can be dragged around on the screen. I was wondering if there is a way to keep the button inside the frame of the view.
I have used this code to make the button drag able:
UIPanGestureRecognizer *buttonPanRecognizer;
buttonPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(setObjectLocation:)];
[NewButton addGestureRecognizer:buttonPanRecognizer];
- (void)setObjectLocation:(UIPanGestureRecognizer *)recognizer {
CGPoint location = [recognizer locationInView:self.view];
if (CGRectContainsPoint([NewButton frame], location)) { // NewButton
NewButton.center = location;
}
else if (CGRectContainsPoint([NewLabel frame], location)) { // NewLabel
NewLabel.center = location;
} }
I also want to be able to keep other kinds of objects inside.
Thansk in advance :)
The problem is that it is possible to drag parts of the UIButton outside the screen.
I have to agree that your problem is not quite clear. What is all the stuff with the label supposed to to. Generally, to keep a button inside a frame, you have everything you need already there:
UIPanGestureRecognizer* buttonPanRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(setObjectLocation:)];
[NewButton addGestureRecognizer:buttonPanRecognizer];
- (void)setObjectLocation:(UIPanGestureRecognizer *)recognizer {
CGPoint location = [recognizer translationInView:self.view];
if (CGRectContainsPoint(self.view.frame, location)) {
NewButton.center = location;
}
}
What's the problem? Just keep the button's origin within a rect (frame.x,frame.y,frame.width - button.width,frame.height - button.height).

Touch events on MKMapView's overlays

In the app I'm currently designing I have a MKMapView with overlays on it (customized MKPolylines btw) and I would like to be able to detect touch events on these overlays and assign a specific action to each overlay. Could any one help me on this one ?
Thanks !
Benja
This can be solved combining How to intercept touches events on a MKMapView or UIWebView objects? and How to determine if an annotation is inside of MKPolygonView (iOS). Add this in viewWillAppear:
WildcardGestureRecognizer * tapInterceptor = [[WildcardGestureRecognizer alloc] init];
tapInterceptor.touchesBeganCallback = ^(NSSet * touches, UIEvent * event) {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.mapView];
CLLocationCoordinate2D coord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(coord);
for (id overlay in self.mapView.overlays)
{
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygon *poly = (MKPolygon*) overlay;
id view = [self.mapView viewForOverlay:poly];
if ([view isKindOfClass:[MKPolygonView class]])
{
MKPolygonView *polyView = (MKPolygonView*) view;
CGPoint polygonViewPoint = [polyView pointForMapPoint:mapPoint];
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polyView.path, NULL, polygonViewPoint, NO);
if (mapCoordinateIsInPolygon) {
debug(#"hit!")
} else {
debug(#"miss!");
}
}
}
}
};
[self.mapView addGestureRecognizer:tapInterceptor];
WildcardGestureRecognizer is in the first linked answer. Calling mapView:viewForOverlay: won't be cheap, adding a local cache of those would help.
Just in case it might help some of you...
I couldn't find a way to do that but I added an annotation on my overlays (Anyway, i needed to do that to display some information) and then I could get the touch event on this annotation. I know it is not the best way to do it but in my situation, and maybe yours, it works ;) !

Resources