Detecting iPhone 5 using pre-processors - objective-c

Is there any way to get the device type as a #define? I want to get this for an Universal app and I want to be able to know if it is an iPad, iPhone4 or iPhone5.
Thanks!

#define IS_WIDESCREEN ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#define IS_IPHONE ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone )
#define IS_IPHONE_5 ( IS_IPHONE && IS_WIDESCREEN )
#define IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

Counter question:
Why use a macro, when there are methods available?
The macro translates into a method call any way, as you cannot know at compile time what device you’ll be confronted with at run time.
The device you’re running on is an iPad, if it says so:
BOOL isThisAnIPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIInterfaceIdiomPad;
Apart from that, you automatically get the right resources by suffixing them with the correct values in combination with the usual #2x for retina graphics.

Related

Creating window from scratch [duplicate]

I'm creating an application in pure C on Mac OSX.
What I want is to create window in witch my app will be stored.
Preferably I want it to be pure C solution, but if I have to use objective-c class to init window and then send context to my C code then it will be fine.
I'm not using xcode, only simple text editor in with I tried to import cocoa but it just generated a lot of errors.
So in summary my question is:
How in simple pure C generate code that will display osx window?
I did a translation of the accepted answer to Pure C:
// based on https://stackoverflow.com/a/30269562
// Minimal Pure C code to create a window in Cocoa
// $ clang minimal.c -framework Cocoa -o minimal.app
#include <objc/runtime.h>
#include <objc/message.h>
#include <Carbon/Carbon.h>
#define cls objc_getClass
#define sel sel_getUid
#define msg ((id (*)(id, SEL, ...))objc_msgSend)
#define cls_msg ((id (*)(Class, SEL, ...))objc_msgSend)
// poor man's bindings!
typedef enum NSApplicationActivationPolicy {
NSApplicationActivationPolicyRegular = 0,
NSApplicationActivationPolicyAccessory = 1,
NSApplicationActivationPolicyERROR = 2,
} NSApplicationActivationPolicy;
typedef enum NSWindowStyleMask {
NSWindowStyleMaskBorderless = 0,
NSWindowStyleMaskTitled = 1 << 0,
NSWindowStyleMaskClosable = 1 << 1,
NSWindowStyleMaskMiniaturizable = 1 << 2,
NSWindowStyleMaskResizable = 1 << 3,
} NSWindowStyleMask;
typedef enum NSBackingStoreType {
NSBackingStoreBuffered = 2,
} NSBackingStoreType;
int main(int argc, char *argv[])
{
// id app = [NSApplication sharedApplication];
id app = cls_msg(cls("NSApplication"), sel("sharedApplication"));
// [app setActivationPolicy:NSApplicationActivationPolicyRegular];
msg(app, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
struct CGRect frameRect = {0, 0, 600, 500};
// id window = [[NSWindow alloc] initWithContentRect:frameRect styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
id window = msg(cls_msg(cls("NSWindow"), sel("alloc")),
sel("initWithContentRect:styleMask:backing:defer:"),
frameRect,
NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable,
NSBackingStoreBuffered,
false);
msg(window, sel("setTitle:"), cls_msg(cls("NSString"), sel("stringWithUTF8String:"), "Pure C App"));
// [window makeKeyAndOrderFront:nil];
msg(window, sel("makeKeyAndOrderFront:"), nil);
// [app activateIgnoringOtherApps:YES];
msg(app, sel("activateIgnoringOtherApps:"), true);
msg(app, sel("run"));
}
You can use Objective-C runtime API example (iOS) Creating an iOS app in pure C
Alternative the same code in obj-c :
echo '#import <Cocoa/Cocoa.h>
int main ()
{
#autoreleasepool{
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
id applicationName = [[NSProcessInfo processInfo] processName];
id window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 120, 120)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
[window setTitle: applicationName];
[window makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
}
return 0;
}' | gcc -fobjc-arc -framework Cocoa -x objective-c -o MicroApp - ; ./MicroApp
This will run Cocoa app with 1 window. Like on screenshot below
You can actually add menu using NSMenu
id applicationMenuBar = [NSMenu new];
id appMenuItem = [NSMenuItem new];
[applicationMenuBar addItem:appMenuItem];
[NSApp setMainMenu: applicationMenuBar];
Can you do this? Yes and no (you can do anything if you're persistent enough). Yes you can, but no you shouldn't. Regardless, this can be done for the incredibly persistent among you. Since coding up an example will take awhile, I found a generous soul on the net who already did it. Look at this repository on GitHub for the full code and explanations. Here are some snippets:
cmacs_simple_msgSend((id)objc_getClass("NSApplication"), sel_getUid("sharedApplication"));
if (NSApp == NULL) {
fprintf(stderr,"Failed to initialized NSApplication... terminating...\n");
return;
}
id appDelObj = cmacs_simple_msgSend((id)objc_getClass("AppDelegate"), sel_getUid("alloc"));
appDelObj = cmacs_simple_msgSend(appDelObj, sel_getUid("init"));
cmacs_void_msgSend1(NSApp, sel_getUid("setDelegate:"), appDelObj);
cmacs_void_msgSend(NSApp, sel_getUid("run"));
As you'll notice, this code uses the Objective-C runtime API to create a faux AppDelegate. And creating the window is an involved process:
self->window = cmacs_simple_msgSend((id)objc_getClass("NSWindow"), sel_getUid("alloc"));
/// Create an instance of the window.
self->window = cmacs_window_init_msgSend(self->window, sel_getUid("initWithContentRect:styleMask:backing:defer:"), (CMRect){0,0,1024,460}, (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask), 0, false);
/// Create an instance of our view class.
///
/// Relies on the view having declared a constructor that allocates a class pair for it.
id view = cmacs_rect_msgSend1(cmacs_simple_msgSend((id)objc_getClass("View"), sel_getUid("alloc")), sel_getUid("initWithFrame:"), (CMRect){ 0, 0, 320, 480 });
// here we simply add the view to the window.
cmacs_void_msgSend1(self->window, sel_getUid("setContentView:"), view);
cmacs_simple_msgSend(self->window, sel_getUid("becomeFirstResponder"));
// Shows our window in the bottom-left hand corner of the screen.
cmacs_void_msgSend1(self->window, sel_getUid("makeKeyAndOrderFront:"), self);
return YES;
So, yes. You can write a Cocoa app in pure C. But I wouldn't recommend it. 90% of that code can be replaced by an xib file, and doing it this way really restricts your app because more advanced features of the Apple development stack really on Objective-C features. While it's technically possible to do everything this way, you're making it much harder than it ought to be.
I remember seeing this question about a year ago, back when I so desperately wished I could open up a d*** window, googling for days and only finding the type of answers you see above this post.
I was reading up on the operating system the Mac is built on - Berkley Software Distribution. http://codex.cs.yale.edu/avi/os-book/OS9/appendices-dir/a.pdf Where on page 17 the phrase "...X Windowing System developed at MIT" hit me and I remembered how I couldn't open up a window and how pissed I was about that, and I thought maybe this was finally the solution!
I googled "BSD X Window Programming" and stumbled my way into finally getting a window open in pure C.
I just discovered it so I'm not a master yet but look at this link https://en.wikibooks.org/wiki/X_Window_Programming/Xlib and go to the example, make sure to follow the comments at the top for how to compile with the X11 library (you can ignore the -Wall and -O commands as long as you have the -lX11).
If you can't compile, if it can't find the header files, you'll need to help it find the header files.
There might be a couple different places that the X11 includes could be on your system. More than likely you'll find it in /opt/X11/include which will have all the definitions of the headers you'll need.
You could include the full path in your C programs such as:
#include "/opt/X11/include/X11/Xlib.h"
But we want it to look like this #include <X11/Xlib.h>
So you could add this switch to GCC when you compile -I /opt/X11/include
Or go to your .profile or .bashrc or .bash_profile in your home directory and add:
export C_INCLUDE_PATH="$C_INCLUDE_PATH:/opt/X11/include"
/*
Simple Xlib application drawing a box in a window.
*/
From the wiki:
#include<X11/Xlib.h>
#include<stdio.h>
#include<stdlib.h> // prevents error for exit on line 18 when compiling with gcc
int main() {
Display *d;
int s;
Window w;
XEvent e;
/* open connection with the server */
d=XOpenDisplay(NULL);
if(d==NULL) {
printf("Cannot open display\n");
exit(1);
}
s=DefaultScreen(d);
/* create window */
w=XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
BlackPixel(d, s), WhitePixel(d, s));
// Process Window Close Event through event handler so XNextEvent does Not fail
Atom delWindow = XInternAtom( d, "WM_DELETE_WINDOW", 0 );
XSetWMProtocols(d , w, &delWindow, 1);
/* select kind of events we are interested in */
XSelectInput(d, w, ExposureMask | KeyPressMask);
/* map (show) the window */
XMapWindow(d, w);
/* event loop */
while(1) {
XNextEvent(d, &e);
/* draw or redraw the window */
if(e.type==Expose) {
XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
}
/* exit on key press */
if(e.type==KeyPress)
break;
// Handle Windows Close Event
if(e.type==ClientMessage)
break;
}
/* destroy our window */
XDestroyWindow(d, w);
/* close connection to server */
XCloseDisplay(d);
return 0;
}
Compile:
gcc -O2 -Wall -o test test.c -L /usr/X11R6/lib -lX11 -lm
Unfortunately the top rated answer doesn't work on new Apple Silicon powered machines due to an ABI mismatch. Basically on ARM64 you can't use the objc_msgSend declaration with variable arguments, you must specify the correct argument types for each call. Here is the version that runs on Apple Silicon:
// based on https://stackoverflow.com/a/59596600/834108
// Minimal Pure C code to create a window in Cocoa
// Adapted to work on ARM64
// $ clang minimal.c -framework Cocoa -o minimal.app
#include <objc/runtime.h>
#include <objc/message.h>
#include <Carbon/Carbon.h>
#define cls objc_getClass
#define sel sel_getUid
#define msg ((id (*)(id, SEL))objc_msgSend)
#define msg_int ((id (*)(id, SEL, int))objc_msgSend)
#define msg_id ((id (*)(id, SEL, id))objc_msgSend)
#define msg_ptr ((id (*)(id, SEL, void*))objc_msgSend)
#define msg_cls ((id (*)(Class, SEL))objc_msgSend)
#define msg_cls_chr ((id (*)(Class, SEL, char*))objc_msgSend)
// poor man's bindings!
typedef enum NSApplicationActivationPolicy {
NSApplicationActivationPolicyRegular = 0,
NSApplicationActivationPolicyAccessory = 1,
NSApplicationActivationPolicyERROR = 2,
} NSApplicationActivationPolicy;
typedef enum NSWindowStyleMask {
NSWindowStyleMaskBorderless = 0,
NSWindowStyleMaskTitled = 1 << 0,
NSWindowStyleMaskClosable = 1 << 1,
NSWindowStyleMaskMiniaturizable = 1 << 2,
NSWindowStyleMaskResizable = 1 << 3,
} NSWindowStyleMask;
typedef enum NSBackingStoreType {
NSBackingStoreBuffered = 2,
} NSBackingStoreType;
int main(int argc, char *argv[])
{
// id app = [NSApplication sharedApplication];
id app = msg_cls(cls("NSApplication"), sel("sharedApplication"));
// [app setActivationPolicy:NSApplicationActivationPolicyRegular];
msg_int(app, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
struct CGRect frameRect = {0, 0, 600, 500};
// id window = [[NSWindow alloc] initWithContentRect:frameRect styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
id window = ((id (*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)(
msg_cls(cls("NSWindow"), sel("alloc")),
sel("initWithContentRect:styleMask:backing:defer:"),
frameRect,
NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable,
NSBackingStoreBuffered,
false
);
msg_id(window, sel("setTitle:"), msg_cls_chr(cls("NSString"), sel("stringWithUTF8String:"), "Pure C App"));
// [window makeKeyAndOrderFront:nil];
msg_ptr(window, sel("makeKeyAndOrderFront:"), nil);
// [app activateIgnoringOtherApps:YES];
msg_int(app, sel("activateIgnoringOtherApps:"), true);
msg(app, sel("run"));
}
Pure C cross-platform example: (Windows/macOS/Linux)
https://nappgui.com/en/demo/products.html
About macOS portability in pure C (updated to BigSur and M1 support):
https://nappgui.com/en/start/win_mac_linux.html#h2
I'm creating an application in pure C on Mac OSX. What I want is to create window in which my app will be stored.
Are you looking for a TTY window?
If so does your application need to create the window?
If not then you can simply write your pure C program and execute it from within Terminal - a TTY environment for "pure C".
If you want a double-clickable app you can write an AppleScript which will open Terminal and run your C. Something like:
tell application "Terminal"
do script "ex /tmp/test; exit"
end tell
This opens a Terminal window showing "ex" and when that quits will terminate the shell process (so no further commands can be typed), but it will not close Terminal itself - for that you will have to work harder.
If you do want you application to create the window itself you either need to write your own simple TTY window, you might find some classes you can use, or you might be able to borrow code from an open source terminal app such as iterm.
HTH

#define or #if statement error

I have read many definitions of # if and # define statement.
I tried to use the way I read, but only with the error "Invalid token at start of a preprocessor expression" on the line that defines it as a comment below:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#define is_ipad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define is_ipad_hd ( (is_ipad == 1 && [UIScreen mainScreen].bounds.size.width > 1024 ) ? YES : NO)
#define is_iphone_hd ([UIScreen mainScreen].bounds.size.width > 480 ? YES : NO)
#define device_width 480.0f
#define device_height 320.0f
#if (is_ipad_hd == YES) // Error here
#define device_width = 2048.0f
#define device_height = 1496.0f
#endif
Why it works in simple tutorials and when we try something more complex these things happen!
These are preprocessor directives, so you don't have access to [UIScreen mainScreen] methods and all other objects that are defined upon compilation!
Macro evaluation happens at compile time.
However, (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) or [UIScreen mainScreen] can only be interpreted at run time.
Beyond that, you should not be relying on the screen size to do your dirty work for you. You should instead be relying on the user interface idiom and the scale of the main screen:
BOOL isiPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
BOOL isHD = [[UIScreen mainScreen] scale] == 2.0;
if (isiPad) {
if (isHD) {
// retina iPad
} else {
// non-retina iPad
}
} else {
if (isHD) {
// retina iPhone/iPod touch
} else {
// non-retina iPhone/iPod touch
}
}
Agree with others here although I'm not as well versed in the C preprocessor a quick googling came back with this:
expression is a C expression of integer type, subject to stringent restrictions. It may contain....
much better formatting than I can quickly achieve here on the source.

Programmatically detect whether an iPad has Retina display?

How can I programmatically (Objective-C) whether an iPad has a Retina display?
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad && [[UIScreen mainScreen] respondsToSelector:#selector(scale)] && [UIScreen mainScreen].scale > 1)
{
// new iPad
}
As other posters have answered, you should check for features rather than models. However, in the few obscure cases where you might want to identify a particular model, you can use the hw.machine sysctrl as follows. Note that if you can't identify the model, it's most likely because your code is running on a new model, so you should do something sensible in that case.
#include <sys/types.h>
#include <sys/sysctl.h>
// Determine the machine name, e.g. "iPhone1,1".
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0); // Get size of data to be returned.
char *name = malloc(size);
sysctlbyname("hw.machine", name, &size, NULL, 0);
NSString *machine = [NSString stringWithCString:name encoding:NSASCIIStringEncoding];
free(name);
Now you can compare "machine" against known values. E.g., to detect iPad (March 2012) models:
if ([machine hasPrefix:#"iPad3,"]) NSLog(#"iPad (March 2012) detected");

Preprocessor-IF doesn't work

I'm trying to check with Preprocessor-Ifs if the Device is an iPad. If it is an iPad, I want to define something Devicespecific, but for some reason I can't check in an PP-IF if a PP-Constant is true.
Maybe you got an idea?
#ifdef UI_USER_INTERFACE_IDIOM
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#else
#define IS_IPAD false
#endif
#if IS_IPAD
#define WIDTH 768
#define HEIGHT 1024
#else
#define WIDTH 320
#define HEIGHT 480
#endif
Preprocessor rules are, (surprise, surprise) processed prior to building the app. Since it's an universal app, it doesn't yet know if it's running on an iPad or an iPhone.
Use this:
#ifdef UI_USER_INTERFACE_IDIOM
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#else
#define IS_IPAD false
#endif
#define WIDTH (IS_IPAD ? 768 : 320)
#define HEIGHT (IS_IPAD ? 1024 : 480)
This is my approach: you can use this in the header file
#define _IPAD ((__IPHONE_OS_VERSION_MAX_ALLOWED >= 30200) && (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad))
#define GUI_TITLE_LABEL_WIDTH (_IPAD? 220*2 : 220)
#define UI_FONT_SIZE (_IPAD? 20 : 16)
Short and easy :D
You have a runtime check inside of a #if statement. A preprocessor check will not evaluate (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) so the width and height will have to be set at runtime since you can not tell if it is an iPad until runtime. I would also recommend using 0 instead of false.
(When you ask a question on SO, you should tell what you tried, and what happened.)
Anyway, I think you will not be able to do what you want there, because at compile time, the compiler does not know what device you will be running on. You could compile the code, and then run on an iPad, and iPhone, an iPod - how could the pre-processor possibly know which device you will be running on in the future?

Checking if UIGraphicsBeginImageContextWithOptions is supported

I'm working on an iOS app. It currently only works on iOS 4 since I use the following method on several occasions: "UIGraphicsBeginImageContextWithOptions". This method is only available in iOS 4 and therefor my app currently crashes/doesn't work on iPhone OS 3. Aside from this method there is no reason why the app should not work on iPhone OS 3. How do I make a check to see wether or not this method is available ? I've tried the following without succes:
if([self respondsToSelector:#selector(UIGraphicsBeginImageContextWithOptions)]) {
UIGraphicsBeginImageContextWithOptions(targetSize, NO, 0.0); // this will crop
}
else
{
UIGraphicsBeginImageContext(targetSize);
}
I've only tried variations like this:
if([self respondsToSelector:#selector(UIGraphicsBeginImageContextWithOptions:size:opaque:scale:)])
and
if([self respondsToSelector:#selector(UIGraphicsBeginImageContextWithOptions:)])
Without succes. Any help would be appreciated.
UIGraphicsBeginImageContextWithOptions is a C function, so you can't use Objective-C methods like -respondsToSelector: to test its existence.
You could, however, weak link the UIKit framework, and then check if UIGraphicsBeginImageContextWithOptions is NULL:
if (UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(...);
} else {
UIGraphicsBeginImageContext(...);
}
I have the same problem. You could try testing the system version. This seems to work for me on the devices I tested.
char majorVersion = [[[UIDevice currentDevice] systemVersion] characterAtIndex: 0];
if (majorVersion == '2' || majorVersion == '3')
UIGraphicsBeginImageContext(...);
else
UIGraphicsBeginImageContextWithOptions(...);
I know this is an old question, but with new Xcode and iOS versions (upper than 9) any of this methods work for me.
I always check the system version in this way:
NSString *sysver = [[UIDevice currentDevice] systemVersion];
NSArray *versionNums = [sysver componentsSeparatedByString:#"."];
int majorVersion = [versionNums[0] intValue];
if (majorVersion > 3){
UIGraphicsBeginImageContextWithOptions(...);
}
else{
UIGraphicsBeginImageContext(...);
}
I hope this could help anyone.