WebView page count - objective-c

I have a WebView that displays some content, which is frequently updated. I would like to display a page counter in my UI which would tell the user how many pages would web view have if they would to print it.
I have tried doing:
NSRange r = NSMakeRange(0, 0);
BOOL knows = [[[[webView mainFrame] frameView] documentView] knowsPageRange: &r];
which gives knows = YES but r.length always equals 1.
How can I get at this information? (Preferably in a performant way, if possible).

Not a perfectly elegant solution, but a possibility could include:
NSString *heightStr = [webView stringByEvaluatingJavaScriptFromString:#"document.body.scrollHeight;"]; // Get the height of our webView
int height = [heightStr intValue];
CGFloat maxHeight = kDefaultPageHeight - 2*kMargin;
CGFloat maxWidth = kDefaultPageWidth - 2*kMargin;
int pages = ceil(height / maxHeight);
return pages;
via Chris Mills' blog.

Related

sprite kit - objective c: slow fps when I create a lot nodes

I wanted to create a space background so I make a for loop to create the stars. Here is the code:
for (int i = 0; i<100; i++) {
SKShapeNode *star= [SKShapeNode shapeNodeWithPath:Path.CGPath];
star.fillColor = [UIColor whiteColor];
star.physicsBody = nil;
int xposition = arc4random()%960;
int yposition = arc4random()%640;
star.position = CGPointMake(xposition, yposition);
float size = (arc4random()%3 + 1)/10.0;
star.xScale = size;
star.yScale = size;
star.alpha = (arc4random()%10 + 1 )/ 10.0;
star.zPosition = -2;
[self addChild:star];
}
But it takes a lot from my cpu. when the code is activated the cpu at top 78%.(I check the code in the iPhone simulator);
Somebody know how to fix it? thanks.
Your physics bodies continue to calculate even when off of the screen. You will need to remove them once they go out of the frame, otherwise everything will slow to a crawl. (And to echo what others have stated you will eventually need a real device).
From this document: Jumping Into Sprite Kit
You can implement the "Did Simulate Physics" method to get rid of the stars that fell from the bottom of the screen like so:
-(void)didSimulatePhysics
{
[self enumerateChildNodesWithName:#"star" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0)
[node removeFromParent];
}];
}
Note that you will first need to set the name of your star shapes by using the name property like so:
star.name = "star"

how to detect free space around object

i've got few objects in view and i want to detect if space (for ex. [myObject frame] - 5px) is free.
now i must detect when my object is clicked but how can i detect free space?
NSString *currPuzzle = [NSString stringWithFormat:#"%d", currentPuzzle];
currentObject = [puzzleArray objectForKey:currPuzzle];
currentObjectFrame = [currentObject frame];
currentObjectCenter = [currentObject center];
int left = currentObjectFrame.origin.x - 1;
int right = currentObjectFrame.origin.x + 1;
int top = currentObjectFrame.origin.y - 1;
int bottom = currentObjectFrame.origin.y + 1;
i've got no idea how solve it.
any idea?
ok i resolve it. i create temporary rectangle and i detect intersect with this rect. all work great.

Route Me: Location & Screen Position

I am drawing a bunch of Markers on my Map View.
The information about the position of all objects (latitude and longitude) is stored in an Array.
To optimize performance i don't want to draw ALL Markers. I only want to alloc Markers in the area i am seeing at the moment. But the only information i get from the Route Me API about position/screen is:
center.latitude;
center.longitude;
But this value is only returning the center of my whole map and i want to see the center position (lat and long) of the actual view. I am also able to get the GPS position, but not the center position of the screen. Do you think there is an easy way to get this information?
This is a part of my implementation:
UIImage* object = [UIImage imageNamed:#"object.png"];
CLLocationCoordinate2D buoyLocation;
NSString* templatString;
NSString* templongString;
for (int i =0; i<(myArray.count); i=i+2)
{
templongString = [myArray objectAtIndex:i];
templatString = [myarray objectAtIndex:i+1];
objectLocation.latitude = [templatString floatValue];
objectLocation.longitude = [templongString floatValue];
myMarker = [[RMMarker alloc] initWithUIImage:object anchorPoint:CGPointMake(xspec, yspec)]; //0.5 1.0
[markerManager1 addMarker:myMarker AtLatLong:objectLocation];
}
Take a look at latitudeLongitudeBoundingBoxForScreen in RMMapContents.
Ciao!
-- Randy

How to get the on-screen location of an NSStatusItem

I have a question about the NSStatusItem for cocoa in mac osx. If you look at the mac app called snippets (see the movie at http://snippetsapp.com/). you will see that once you clicked your statusbar icon that a perfectly aligned view / panel or maybe even windows appears just below the icon.
My question is ... How to calculate the position to where to place your NSWindow just like this app does?
I have tried the following:
Subclass NSMenu
Set the view popery for the first item of the menu (Worked but enough)
Using addSubview instead of icon to NSStatusItem this worked but could not get higher then 20px
Give the NSStatusItem a view, then get the frame of that view's window. This technically counts as UndocumentedGoodness, so don't be surprised if it breaks someday (e.g., if they start keeping the window offscreen instead).
I don't know what you mean by “could not get heigher then 20px”.
To do this without the hassle of a custom view, I tried the following (that works). In the method that is set as the action for the status item i.e. the method that is called when the user clicks the status item, the frame of the status item can be retrieved by:
[[[NSApp currentEvent] window] frame]
Works a treat for me
Given an NSMenuItem and an NSWindow, you can get the point that centers your window right below the menu item like this:
fileprivate var centerBelowMenuItem: CGPoint {
guard let window = window, let barButton = statusItem.button else { return .zero }
let rectInWindow = barButton.convert(barButton.bounds, to: nil)
let screenRect = barButton.window?.convertToScreen(rectInWindow) ?? .zero
// We now have the menu item rect on the screen.
// Let's do some basic math to center our window to this point.
let centerX = screenRect.origin.x-(window.frame.size.width-barButton.bounds.width)/2
return CGPoint(x: centerX, y: screenRect.origin.y)
}
No need for undocumented API's.
Maybe another solution which works for me (swift 4.1) :
let yourStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let frameOrigin = yourStatusItem.button?.window?.frame.origin
let yourPoint = CGPoint(x: (frameOrigin?.x)!, y: (frameOrigin?.y)! - 22)
yourWindow?.setFrameOrigin(yourPoint)
It seems that this app uses Matt's MAAttachedWindow. There's an sample application with the same layout & position.
NOTE: PLEASE DO NOT USE THIS, at least not for the purpose of locating an NSStatusItem.
Back when I posted this, this crazy image matching technique was the only way to solve this problem without undocumented API. Now, you should use Oskar's solution.
If you're willing to use image analysis to find the status item on a menu bar, here's a category for NSScreen which does exactly that.
It might seem crazy to do it this way, but it's fast, relatively small, and it's the only way of finding a status item without undocumented API.
If you pass in the current image for the status item, this method should find it.
#implementation NSScreen (LTStatusItemLocator)
// Find the location of IMG on the screen's status bar.
// If the image is not found, returns NSZeroPoint
- (NSPoint)originOfStatusItemWithImage:(NSImage *)IMG
{
CGColorSpaceRef csK = CGColorSpaceCreateDeviceGray();
NSPoint ret = NSZeroPoint;
CGDirectDisplayID screenID = 0;
CGImageRef displayImg = NULL;
CGImageRef compareImg = NULL;
CGRect screenRect = CGRectZero;
CGRect barRect = CGRectZero;
uint8_t *bm_bar = NULL;
uint8_t *bm_bar_ptr;
uint8_t *bm_compare = NULL;
uint8_t *bm_compare_ptr;
size_t bm_compare_w, bm_compare_h;
BOOL inverted = NO;
int numberOfScanLines = 0;
CGFloat *meanValues = NULL;
int presumptiveMatchIdx = -1;
CGFloat presumptiveMatchMeanVal = 999;
// If the computer is set to Dark Mode, set the "inverted" flag
NSDictionary *globalPrefs = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain];
id style = globalPrefs[#"AppleInterfaceStyle"];
if ([style isKindOfClass:[NSString class]]) {
inverted = (NSOrderedSame == [style caseInsensitiveCompare:#"dark"]);
}
screenID = (CGDirectDisplayID)[self.deviceDescription[#"NSScreenNumber"] integerValue];
screenRect = CGDisplayBounds(screenID);
// Get the menubar rect
barRect = CGRectMake(0, 0, screenRect.size.width, 22);
displayImg = CGDisplayCreateImageForRect(screenID, barRect);
if (!displayImg) {
NSLog(#"Unable to create image from display");
CGColorSpaceRelease(csK);
return ret; // I would normally use goto(bail) here, but this is public code so let's not ruffle any feathers
}
size_t bar_w = CGImageGetWidth(displayImg);
size_t bar_h = CGImageGetHeight(displayImg);
// Determine scale factor based on the CGImageRef we got back from the display
CGFloat scaleFactor = (CGFloat)bar_h / (CGFloat)22;
// Greyscale bitmap for menu bar
bm_bar = malloc(1 * bar_w * bar_h);
{
CGContextRef bmCxt = NULL;
bmCxt = CGBitmapContextCreate(bm_bar, bar_w, bar_h, 8, 1 * bar_w, csK, kCGBitmapAlphaInfoMask&kCGImageAlphaNone);
// Draw the menu bar in grey
CGContextDrawImage(bmCxt, CGRectMake(0, 0, bar_w, bar_h), displayImg);
uint8_t minVal = 0xff;
uint8_t maxVal = 0x00;
// Walk the bitmap
uint64_t running = 0;
for (int yi = bar_h / 2; yi == bar_h / 2; yi++)
{
bm_bar_ptr = bm_bar + (bar_w * yi);
for (int xi = 0; xi < bar_w; xi++)
{
uint8_t v = *bm_bar_ptr++;
if (v < minVal) minVal = v;
if (v > maxVal) maxVal = v;
running += v;
}
}
running /= bar_w;
uint8_t threshold = minVal + ((maxVal - minVal) / 2);
//threshold = running;
// Walk the bitmap
bm_bar_ptr = bm_bar;
for (int yi = 0; yi < bar_h; yi++)
{
for (int xi = 0; xi < bar_w; xi++)
{
// Threshold all the pixels. Values > 50% go white, values <= 50% go black
// (opposite if Dark Mode)
// Could unroll this loop as an optimization, but probably not worthwhile
*bm_bar_ptr = (*bm_bar_ptr > threshold) ? (inverted?0x00:0xff) : (inverted?0xff:0x00);
bm_bar_ptr++;
}
}
CGImageRelease(displayImg);
displayImg = CGBitmapContextCreateImage(bmCxt);
CGContextRelease(bmCxt);
}
{
CGContextRef bmCxt = NULL;
CGImageRef img_cg = NULL;
bm_compare_w = scaleFactor * IMG.size.width;
bm_compare_h = scaleFactor * 22;
// Create out comparison bitmap - the image that was passed in
bmCxt = CGBitmapContextCreate(NULL, bm_compare_w, bm_compare_h, 8, 1 * bm_compare_w, csK, kCGBitmapAlphaInfoMask&kCGImageAlphaNone);
CGContextSetBlendMode(bmCxt, kCGBlendModeNormal);
NSRect imgRect_og = NSMakeRect(0,0,IMG.size.width,IMG.size.height);
NSRect imgRect = imgRect_og;
img_cg = [IMG CGImageForProposedRect:&imgRect context:nil hints:nil];
CGContextClearRect(bmCxt, imgRect);
CGContextSetFillColorWithColor(bmCxt, [NSColor whiteColor].CGColor);
CGContextFillRect(bmCxt, CGRectMake(0,0,9999,9999));
CGContextScaleCTM(bmCxt, scaleFactor, scaleFactor);
CGContextTranslateCTM(bmCxt, 0, (22. - IMG.size.height) / 2.);
// Draw the image in grey
CGContextSetFillColorWithColor(bmCxt, [NSColor blackColor].CGColor);
CGContextDrawImage(bmCxt, imgRect, img_cg);
compareImg = CGBitmapContextCreateImage(bmCxt);
CGContextRelease(bmCxt);
}
{
// We start at the right of the menu bar, and scan left until we find a good match
int numberOfScanLines = barRect.size.width - IMG.size.width;
bm_compare = malloc(1 * bm_compare_w * bm_compare_h);
// We use the meanValues buffer to keep track of how well the image matched for each point in the scan
meanValues = calloc(sizeof(CGFloat), numberOfScanLines);
// Walk the menubar image from right to left, pixel by pixel
for (int scanx = 0; scanx < numberOfScanLines; scanx++)
{
// Optimization, if we recently found a really good match, bail on the loop and return it
if ((presumptiveMatchIdx >= 0) && (scanx > (presumptiveMatchIdx + 5))) {
break;
}
CGFloat xOffset = numberOfScanLines - scanx;
CGRect displayRect = CGRectMake(xOffset * scaleFactor, 0, IMG.size.width * scaleFactor, 22. * scaleFactor);
CGImageRef displayCrop = CGImageCreateWithImageInRect(displayImg, displayRect);
CGContextRef compareCxt = CGBitmapContextCreate(bm_compare, bm_compare_w, bm_compare_h, 8, 1 * bm_compare_w, csK, kCGBitmapAlphaInfoMask&kCGImageAlphaNone);
CGContextSetBlendMode(compareCxt, kCGBlendModeCopy);
// Draw the image from our menubar
CGContextDrawImage(compareCxt, CGRectMake(0,0,IMG.size.width * scaleFactor, 22. * scaleFactor), displayCrop);
// Blend mode difference is like an XOR
CGContextSetBlendMode(compareCxt, kCGBlendModeDifference);
// Draw the test image. Because of blend mode, if we end up with a black image we matched perfectly
CGContextDrawImage(compareCxt, CGRectMake(0,0,IMG.size.width * scaleFactor, 22. * scaleFactor), compareImg);
CGContextFlush(compareCxt);
// Walk through the result image, to determine overall blackness
bm_compare_ptr = bm_compare;
for (int i = 0; i < bm_compare_w * bm_compare_h; i++)
{
meanValues[scanx] += (CGFloat)(*bm_compare_ptr);
bm_compare_ptr++;
}
meanValues[scanx] /= (255. * (CGFloat)(bm_compare_w * bm_compare_h));
// If the image is very dark, it matched well. If the average pixel value is < 0.07, we consider this
// a presumptive match. Mark it as such, but continue looking to see if there's an even better match.
if (meanValues[scanx] < 0.07) {
if (meanValues[scanx] < presumptiveMatchMeanVal) {
presumptiveMatchMeanVal = meanValues[scanx];
presumptiveMatchIdx = scanx;
}
}
CGImageRelease(displayCrop);
CGContextRelease(compareCxt);
}
}
// After we're done scanning the whole menubar (or we bailed because we found a good match),
// return the origin point.
// If we didn't match well enough, return NSZeroPoint
if (presumptiveMatchIdx >= 0) {
ret = CGPointMake(CGRectGetMaxX(self.frame), CGRectGetMaxY(self.frame));
ret.x -= (IMG.size.width + presumptiveMatchIdx);
ret.y -= 22;
}
CGImageRelease(displayImg);
CGImageRelease(compareImg);
CGColorSpaceRelease(csK);
if (bm_bar) free(bm_bar);
if (bm_compare) free(bm_compare);
if (meanValues) free(meanValues);
return ret;
}
#end
From the Apple NSStatusItem Class Reference:
Setting a custom view overrides all the other appearance and behavior settings defined by NSStatusItem. The custom view is responsible for drawing itself and providing its own behaviors, such as processing mouse clicks and sending action messages.

NSImage acting weird

Why is this code setting artistImage to an image with 0 width and 0 height?
NSURL *artistImageURL = [NSURL URLWithString:#"http://userserve-ak.last.fm/serve/252/8581581.jpg"];
NSImage *artistImage = [[NSImage alloc] initWithContentsOfURL:artistImageURL];
As Ken wrote, the DPI is messed up in this image. If you want to force NSImage to set the real image size (ignoring the DPI), use the method described at http://borkware.com/quickies/one?topic=NSImage:
NSBitmapImageRep *rep = [[image representations] objectAtIndex: 0];
NSSize size = NSMakeSize([rep pixelsWide], [rep pixelsHigh]);
[image setSize: size];
NSImage does load this fine for me, but that particular image has corrupt metadata. Its resolution according to the exif data is 7.1999997999228071e-06 dpi.
NSImage respects the DPI info in the file, so if you try to draw the image at its natural size, you'll get something 2520000070 pixels across.
Last I checked, NSImage's -initWithContentsOfURL: only works with file URLs. You'll need to retrieve the URL first, and then use -initWithData:
It is more or less guaranteed that .representations contains NSImageRep* (of course not always NSBitmapImageRep). To be on a safe side for future extensions one can write something like code below. And it also takes into account multiple representation (like in some .icns and .tiff files).
#implementation NSImage (Extension)
- (void) makePixelSized {
NSSize max = NSZeroSize;
for (NSObject* o in self.representations) {
if ([o isKindOfClass: NSImageRep.class]) {
NSImageRep* r = (NSImageRep*)o;
if (r.pixelsWide != NSImageRepMatchesDevice && r.pixelsHigh != NSImageRepMatchesDevice) {
max.width = MAX(max.width, r.pixelsWide);
max.height = MAX(max.height, r.pixelsHigh);
}
}
}
if (max.width > 0 && max.height > 0) {
self.size = max;
}
}
#end