Arial Black Italic font causing WinForms application to crash - winforms

A WinForms application that I maintain crashes on a very small fraction of user machines (probably about 4 to date). The application crashes every time for those users, and it crashes before the very first dialog gets displayed.
Exception
Source:
System.Drawing
Message:
Font 'Arial Black' does not support style 'Bold'.
Stack Trace:
at System.Drawing.Font.CreateNativeFont()
at System.Drawing.Font.Initialize(FontFamily family, Single emSize, FontStyle style, GraphicsUnit unit, Byte gdiCharSet, Boolean gdiVerticalFont)
at System.Drawing.Font.Initialize(String familyName, Single emSize, FontStyle style, GraphicsUnit unit, Byte gdiCharSet, Boolean gdiVerticalFont)
at System.Drawing.Font..ctor(String familyName, Single emSize, FontStyle style, GraphicsUnit unit, Byte gdiCharSet)
One of the fonts that the application uses is Arial Black:
this.label3.Font = new System.Drawing.Font("Arial Black", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
The first time this crash happened, I noticed a font that was on the user's computer, but not mine. It was called “Arial Black Italic” and it was dated 1997. This was the filename:
ARBLI___.TTF
The user had Windows XP.
I deleted the font, and afterwards the application ran fine. As I mentioned, over the past 22 months, this crash happened to about 3 other users. Each time deleting the “Arial Black Italic” font from the user's computer seemed to fix the problem.
The most recent time, the user had Windows 7 and the font was date much newer, yet the aforementioned protocol still resolved the problem.
At this point, I am trying to figure out the root cause of this crash bug and how to prevent it.

Try something like this.
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Create regular font first.
// Depending on the user's system, this font may already be bold.
//
var theFont = new System.Drawing.Font(
"Arial Black",
8.25F,
System.Drawing.FontStyle.Regular,
System.Drawing.GraphicsUnit.Point,
( ( byte )( 0 ) )
);
// If font is not bold, then try to create it.
//
if ( ( null != theFont ) && !theFont.Bold )
{
if ( theFont.FontFamily.IsStyleAvailable( FontStyle.Bold ) )
{
theFont = new Font( theFont, FontStyle.Bold );
}
}
// Now use the font.
//
this.label3.Font = theFont;
}
}
}

Related

Screenshot WPF app not working on second monitor

There are other similar questions, but they deal with only screenshotting the displayed application. My application is transparent, so I need to take a screenshot of both the displayed window and the background behind it.
This function correctly screenshots the app on my main monitor, but when it goes to the other monitor, the screenshots are the wrong shape and are completely black. If the window is partly on the monitor, then it still works, but as soon as the other monitor takes control of the window, I get black screenshots. How do I get the screenshots to render correctly?
Here is my function:
public void SaveSnapshot2(int count)
{
string imageName = "image" + count.ToString() + ".jpeg";
string basePath = Path.GetFullPath(Environment.CurrentDirectory);
string folderPath = Path.Combine(basePath, "Snapshots");
string fullPath = Path.Combine(folderPath, imageName);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
System.Windows.Point relativeWindowPosition = App.Current.MainWindow.PointToScreen(new System.Windows.Point(0, 0));
System.Windows.Point relativeBottomRightWindowPosition = App.Current.MainWindow.PointToScreen(new System.Windows.Point(App.Current.MainWindow.Width, App.Current.MainWindow.Height));
System.Windows.Point actualWidthHeight = new System.Windows.Point((relativeBottomRightWindowPosition.X - relativeWindowPosition.X), (relativeBottomRightWindowPosition.Y - relativeWindowPosition.Y));
System.Drawing.Size convertedSize = new System.Drawing.Size((int)actualWidthHeight.X, (int)actualWidthHeight.Y);
Bitmap Screenshot = new Bitmap((int)actualWidthHeight.X, (int)actualWidthHeight.Y, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(Screenshot))
{
// Crops the screenshot
// The relativePoint is where the top left corner of the image is. This is correct.
g.CopyFromScreen((int)relativeWindowPosition.X, (int)relativeWindowPosition.Y, 0, 0, convertedSize); // or (0, 0, 0, 0, Screenshot.Size) to get the whole bitmap image
}
try
{
Screenshot.Save(fullPath, ImageFormat.Jpeg);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + ", the image did not save.");
}
Screenshot.Dispose();
}
Coming up with names for the points was difficult. I hope they are clear enough.
Does the coordinate system change when a window is on a separate monitor? Am I going about this the right way?
I guess you have not declared DPI awareness in the application manifest. It's a common pitfall when you have multiple monitors of different DPI. If have not, most methods and functions related to screen coordinates will return incorrect values and thus the calculation based on those values will be screwed up.
To solve this, add the application manifest which declares DPI awareness. See Setting the default DPI awareness for a process. PerMonitorV2 is the key for Per-Monitor DPI.

Layout issue with Codename One

At this moment I'm only testing my app in the simulator (as I'm having issues with "Send iOS Build" mentioned in another thread [Errors with Codename One "Send iOS Build" and "Send Android Build")
I'm experiencing some layout issues where it is not making use of the width and height correctly. The elements are left-aligned and there is unused space on the right side. And I need to scroll up and down instead of having everything fit within the visual area. Please see images.
The code are:
private final void show() {
loginSignupForm = new Form("Company", new BoxLayout(0));
Tabs loginSignupTabs = new Tabs();
Style loginSignupStyle = UIManager.getInstance().getComponentStyle("Tab");
prepareAndAddSignupTab(loginSignupTabs, loginSignupStyle);
prepareAndAddLoginTab(loginSignupTabs, loginSignupStyle);
loginSignupForm.add(loginSignupTabs);
loginSignupForm.show();
}
private void prepareAndAddLoginTab(Tabs loginSignupTabs, Style loginSignupStyle) {
loginID = new TextField();
loginPassword = new TextField();
Button loginButton = getLoginButton();
Component[] loginComponents = {
new Label("Email Address"),
loginID,
new Label("Password"),
loginPassword,
loginButton,
};
Container loginContainer = BoxLayout.encloseY(loginComponents);
FontImage loginIcon = FontImage.createMaterial(FontImage.MATERIAL_QUESTION_ANSWER, loginSignupStyle);
loginSignupTabs.addTab("Login", loginIcon, loginContainer);
}
What do I need to changenter code heree to get the elements to:
1. expand to the maximum width (no free space on the right)
2. fit within the visual area (for top-to-bottom)
Please note that I'm coding the elements because I find the (new) GUI Builder quite a challenge to use.
Firstly, don't pass a constant value as an argument to Layouts, coz the values might change in future Codename One updates and this will be difficult for you to debug. new BoxLayout(0) should be new BoxLayout(BoxLayout.Y_AXIS) or simply BoxLayout.y().
The above is where the problem arose but not the only problem because BoxLayout doesn't recognize 0 as a valid argument as it has only 3 which are X_AXIS = 1, Y_AXIS = 2, and X_AXIS_NO_GROW = 3.
If you change the above to use BoxLayout.Y_AXIS, it will work, but from the screenshot above, that's not the best solution.
In conclusion, change your code to below:
private final void show() {
loginSignupForm = new Form("Company", new BorderLayout());
Tabs loginSignupTabs = new Tabs();
Style loginSignupStyle = UIManager.getInstance().getComponentStyle("Tab");
prepareAndAddSignupTab(loginSignupTabs, loginSignupStyle);
prepareAndAddLoginTab(loginSignupTabs, loginSignupStyle);
loginSignupForm.add(BorderLayout.CENTER, loginSignupTabs);
loginSignupForm.show();
}

Identical fonts don't look identical on high DPI monitors

I've got a WinForms application that works great on older systems, but I'm having trouble making it look good on 4k monitors. There are multiple issues, and a lot written on the subject, but this question is focused on one specific problem. I can set different controls to use the same font, but on high DPI systems, the controls will look a lot different. How can I fix this?
Obviously I can change the font size, move controls around, etc. But Windows is adding a mysterious factor into my font sizes. Without knowing what Windows is doing, it's hard for me to undo it!
On an older system my test window looks perfect:
On a high DPI system, some controls have a different font size than others:
I've tried several things, including manually setting the font on some controls rather than inheriting from the form. As you can see, changing the font did not fix the problem:
After searching the Internet I've tried several things to fix this including:
Changing the application between PROCESS_DPI_UNAWARE, PROCESS_SYSTEM_DPI_AWARE, and PROCESS_PER_MONITOR_DPI_AWARE
Explicitly changing the font rather than using the form's font.
Building on an old system vs building on a high DPI system.
Building on a monitor set to 96 DPI / 100% vs building on a monitor set to 192 DPI / 200% on the same computer.
Building the form in visual studio's designer vs building it in pure C# code.
.Net 4.0 vs. .Net 4.6.1
Visual Studio 2010 vs Visual Studio 2015
I only found one thing that fixed my problem. Unfortunately I had to do it on the target machine, not on the machine where I'm building this. So it's not a practical solution. See the second item under "steps to repeat" for more details.
Steps to repeat:
This happens with a lot of controls on a lot of forms. See the code sample below for a small, simple demo. That's how I got the screenshots, above.
I can make this problem appear or disappear with one system setting. If you change the main monitor to 96 DPI / 100% scaling, then reboot, you'll get the good result where all fonts are as requested. If you change the main monitor to a different DPI setting, then reboot, you'll see the bad results.
private void newFormButton_Click(object sender, EventArgs e)
{
Font copyOfFont = new Font(Font, FontStyle.Strikeout);
Form form = new Form();
form.Font = Font;
string sample = "Abc 123 :)";
int padding = 6;
Label label = new Label();
label.Text = sample;
label.Top = padding;
label.Left = padding;
label.Font = copyOfFont;
label.Parent = form;
Button button = new Button();
button.Text = sample;
button.Top = label.Bottom + padding;
button.Left = padding;
button.Width = label.Width + padding * 2;
button.Height = label.Height + padding * 2;
button.Parent = form;
TextBox textBox = new TextBox();
textBox.Text = sample;
textBox.Size = button.Size;
textBox.Top = button.Bottom + padding;
textBox.Left = padding;
textBox.Parent = form;
ListBox listBox = new ListBox();
listBox.Items.Add(sample);
listBox.Items.Add(sample);
listBox.Width = button.Width;
listBox.Height = button.Height * 2;
listBox.Top = textBox.Bottom + padding;
listBox.Left = padding;
listBox.Font = copyOfFont;
listBox.Parent = form;
form.Show();
}
This is crazy but it works.
Everything I've seen on the internet about DPI Virtualization says that Windows will automatically set a process to PROCESS_DPI_UNAWARE by default. So unless you explicitly pick one of the other two settings, your application should look decent on a high resolution monitor. It might be a little fuzzy, but it shouldn't look as bad as the examples I've shown above.
Apparently that's not true. The default depends on the computer, and it depends on the day. My solution: Explicitly set the application to use PROCESS_DPI_UNAWARE. I've included a code sample below.
Note that you should be able to take care of this using the manifest. Some sources say that's the preferred way, rather than using C# code. We've had mixed results with that. The C# code option seems more reliable.
[DllImport("shcore.dll")]
static extern int SetProcessDpiAwareness(_Process_DPI_Awareness value);
enum _Process_DPI_Awareness
{
Process_DPI_Unaware = 0,
Process_System_DPI_Aware = 1,
Process_Per_Monitor_DPI_Aware = 2
}
public MainForm()
{
//int result = SetProcessDpiAwareness(_Process_DPI_Awareness.Process_System_DPI_Aware);
//int result = SetProcessDpiAwareness(_Process_DPI_Awareness.Process_Per_Monitor_DPI_Aware);
int result = SetProcessDpiAwareness(_Process_DPI_Awareness.Process_DPI_Unaware);
System.Diagnostics.Debug.Assert(result == 0);
This works on a number of different developer machines. We're about to start sending the fix out to beta testers.
Summary
The O/S provides a compatibility mode for old programs running on high DPI systems.
WinForms and the O/S provide tools for manually changing the sizes of your controls depending on the DPI of the system
or the current monitor.
Both #1 and #2 are both seriously buggy!
The details var a lot from one computer to the next.
Fixing #2 would be the more
powerful option, but as far as I can tell it would be impossible to
fix that.
Instead I fixed #1. That works reasonably well.

Null values for PageMediaSize of a PrintTicket with Adobe PDF Creator using custom page size

I'm writing a WPF application to get and save print profiles (and another application using this print profiles to print documents).
It works fine except when I try to use Adobe PDF Creator has a printer with custom size (like 800mm by 1200mm). Then the PageMediaSize Width and Height are null in the print ticket.
Here's the code I use to get the PrintTicket :
PrintDialog pd = new PrintDialog();
if (pd.ShowDialog() == true)
{
PrintDocument doc = new PrintDocument();
doc.PrintPage += (o, a) =>
{
PrintQueue pq = pd.PrintQueue;
PrintTicket ticket = pd.PrintTicket;
...
a.Cancel = true;
};
doc.Print();
}
The PrindDialog contains the correct width and height for the page, but if I try to use the PrintTicket to print a document it crash, stating that PageMediaSize cannot contains null values.
Anyone have an idea on how to get a working PrintTicket ?
It sounds like a problem with Adobe PDF Creator's driver. The driver provides values for all the sizes, so if there is a problem, you might want to contact the makers of the printer/driver.
A way around this would be to figure out the sizes (800mm x 1200mm) and assume a resolution of 1/96 inches. Then do a conversion:
Width: ( 800mm) / (25.4 mm/in) / (1/96in) = 3023.62
Height: (1200mm) / (25.4 mm/in) / (1/96in) = 4535.43
And with those values you can say:
pd.PrintTicket.PageMediaSize = new PageMediaSize(Width, Height);

iOS 6: Porting iPhone 4 application to iPhone 5 [duplicate]

The new iPhone 5 display has a new aspect ratio and a new resolution (640 x 1136 pixels).
What is required to develop new or transition already existing applications to the new screen size?
What should we keep in mind to make applications "universal" for both the older displays and the new widescreen aspect ratio?
Download and install latest version of Xcode.
Set a Launch Screen File for your app (in the general tab of your target settings). This is how you get to use the full size of any screen, including iPad split view sizes in iOS 9.
Test your app, and hopefully do nothing else, since everything should work magically if you had set auto resizing masks properly, or used Auto Layout.
If you didn't, adjust your view layouts, preferably with Auto Layout.
If there is something you have to do for the larger screens specifically, then it looks like you have to check height of [[UIScreen mainScreen] bounds] as there seems to be no specific API for that. As of iOS 8 there are also size classes that abstract screen sizes into regular or compact vertically and horizontally and are recommended way to adapt your UI.
If you have an app built for iPhone 4S or earlier, it'll run letterboxed on iPhone 5.
To adapt your app to the new taller screen, the first thing you do is to change the launch image to: Default-568h#2x.png. Its size should be 1136x640 (HxW). Yep, having the default image in the new screen size is the key to let your app take the whole of new iPhone 5's screen.
(Note that the naming convention works only for the default image. Naming another image "Image-568h#2x.png" will not cause it to be loaded in place of "Image#2x.png". If you need to load different images for different screen sizes, you'll have to do it programmatically.)
If you're very very lucky, that might be it... but in all likelihood, you'll have to take a few more steps.
Make sure, your Xibs/Views use auto-layout to resize themselves.
Use springs and struts to resize views.
If this is not good enough for your app, design your xib/storyboard
for one specific screen size and reposition programmatically for the
other.
In the extreme case (when none of the above suffices), design the two Xibs and load the appropriate one in the view controller.
To detect screen size:
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
    CGSize result = [[UIScreen mainScreen] bounds].size;
    if(result.height == 480)
{
// iPhone Classic
    }
    if(result.height == 568)
{
// iPhone 5
    }
}
The only really required thing to do is to add a launch image named "Default-568h#2x.png" to the app resources, and in general case (if you're lucky enough) the app will work correctly.
In case the app does not handle touch events, then make sure that the key window has the proper size. The workaround is to set the proper frame:
[window setFrame:[[UIScreen mainScreen] bounds]]
There are other issues not related to screen size when migrating to iOS 6. Read iOS 6.0 Release Notes for details.
Sometimes (for pre-storyboard apps), if the layout is going to be sufficiently different, it's worth specifying a different xib according to device (see this question - you'll need to modify the code to deal with iPhone 5) in the viewController init, as no amount of twiddling with autoresizing masks will work if you need different graphics.
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
NSString *myNibName;
if ([MyDeviceInfoUtility isiPhone5]) myNibName = #"MyNibIP5";
else myNibName = #"MyNib";
if ((self = [super initWithNibName:myNibName bundle:nibBundleOrNil])) {
...
This is useful for apps which are targeting older iOS versions.
Here you can find a nice tutorial (for MonoTouch, but you can use the information for Non-MonoTouch-projects, too):
http://redth.info/get-your-monotouch-apps-ready-for-iphone-5-ios-6-today/
Create a new image for your splash/default screen (640 x 1136 pixel) with the name "Default-568h#2x.png"
In the iOS Simulator, go to the Hardware -> Device menu, and select "iPhone (Retina 4-inch)"
Create other images, e.g. background images
Detect iPhone 5 to load your new images:
public static bool IsTall
{
get {
return UIDevice.currentDevice.userInterfaceIdiom
== UIUserInterfaceIdiomPhone
&& UIScreen.mainScreen.bounds.size.height
* UIScreen.mainScreen.scale >= 1136;
}
}
private static string tallMagic = "-568h#2x";
public static UIImage FromBundle16x9(string path)
{
//adopt the -568h#2x naming convention
if(IsTall())
{
var imagePath = Path.GetDirectoryName(path.ToString());
var imageFile = Path.GetFileNameWithoutExtension(path.ToString());
var imageExt = Path.GetExtension(path.ToString());
imageFile = imageFile + tallMagic + imageExt;
return UIImage.FromFile(Path.Combine(imagePath,imageFile));
}
else
{
return UIImage.FromBundle(path.ToString());
}
}
It's easy for migrating iPhone5 and iPhone4 through XIBs.........
UIViewController *viewController3;
if ([[UIScreen mainScreen] bounds].size.height == 568)
{
UIViewController *viewController3 = [[[mainscreenview alloc] initWithNibName:#"iphone5screen" bundle:nil] autorelease];
}
else
{
UIViewController *viewController3 = [[[mainscreenview alloc] initWithNibName:#"iphone4screen" bundle:nil] autorelease];
}
I solve this problem here. Just add ~568h#2x suffix to images and ~568h to xib's. No needs more runtime checks or code changes.
I had added the new default launch image and (in checking out several other SE answers...) made sure my storyboards all auto-sized themselves and subviews but the retina 4 inches still letterboxed.
Then I noticed that my info plist had a line item for "Launch image" set to "Default.png", which I thusly removed and magically letterboxing no longer appeared. Hopefully, that saves someone else the same craziness I endured.
I guess, it is not going to work in all cases, but in my particular project it avoided me from duplication of NIB-files:
Somewhere in common.h you can make these defines based off of screen height:
#define HEIGHT_IPHONE_5 568
#define IS_IPHONE ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_5 ([[UIScreen mainScreen] bounds ].size.height == HEIGHT_IPHONE_5)
In your base controller:
- (void)viewDidLoad
{
[super viewDidLoad];
if (IS_IPHONE_5) {
CGRect r = self.view.frame;
r.size.height = HEIGHT_IPHONE_5 - 20;
self.view.frame = r;
}
// now the view is stretched properly and not pushed to the bottom
// it is pushed to the top instead...
// other code goes here...
}
In a constants.h file you can add these define statements:
#define IS_IPAD UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
#define IS_IPHONE UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone
#define IS_WIDESCREEN (fabs((double)[[UIScreen mainScreen] bounds].size.height - (double)568) < DBL_EPSILON)
#define IS_IPHONE_5 (!IS_IPAD && IS_WIDESCREEN)
To determine if your app can support iPhone 5 Retina use this:
(This could be more robust to return the type of display, 4S Retina, etc., but as it is written below, it just returns if the iPhone supports iOS5 Retina as a YES or NO)
In a common ".h" file add:
BOOL IS_IPHONE5_RETINA(void);
In a common ".m" file add:
BOOL IS_IPHONE5_RETINA(void) {
BOOL isiPhone5Retina = NO;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
if ([UIScreen mainScreen].scale == 2.0f) {
CGSize result = [[UIScreen mainScreen] bounds].size;
CGFloat scale = [UIScreen mainScreen].scale;
result = CGSizeMake(result.width * scale, result.height * scale);
if(result.height == 960){
//NSLog(#"iPhone 4, 4s Retina Resolution");
}
if(result.height == 1136){
//NSLog(#"iPhone 5 Resolution");
isiPhone5Retina = YES;
}
} else {
//NSLog(#"iPhone Standard Resolution");
}
}
return isiPhone5Retina;
}
First of all create two xibs and attach all delegates,main class to the xib and then u can put in this condition mentioned below in your appdelegate.m file in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
if ([[UIScreen mainScreen] bounds].size.height == 568)
{
self.ViewController = [[ViewController alloc] initWithNibName:#"ViewControlleriphone5" bundle:nil];
}
else
{
self.ViewController = [[ViewController alloc] initWithNibName:#"ViewControlleriphone4" bundle:nil];
}
you can use it any where in the program depending upon your requirements even in your ViewController classes. What matters the most is that you have created two xib files separate for iphone 4(320*480) and iphone 5(320*568)
Try the below method in a singleton class:
-(NSString *)typeOfDevice
{
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
CGSize result = [[UIScreen mainScreen] bounds].size;
if(result.height == 480)
{
return #"Iphone";
}
if(result.height == 568)
{
return #"Iphone 5";
}
}
else{
return #"Ipad";;
}
return #"Iphone";
}
You can use the Auto Layout feature and create the design using iPhone 5 screen resolution and it will work for the both 4" and 3.5" devices, but in this case you should have a enough knowledge of layout manager.
Checking bounds with 568 will fail in landscape mode. iPhone 5 launches only in portrait mode but if you want to support rotations then the iPhone 5 "check" will need to handle this scenario as well.
Here's a macro which handles orientation state:
#define IS_IPHONE_5 (CGSizeEqualToSize([[UIScreen mainScreen] preferredMode].size, CGSizeMake(640, 1136)))
The use of the 'preferredMode' call is from another posting I read a few hours ago so I did not come up with this idea.
First show this image. In that image you show warning for Retina 4 support so click on this warning and click on add so your Retina 4 splash screen automatically add in your project.
and after you use this code :
if([[UIScreen mainScreen] bounds].size.height == 568)
{
// For iphone 5
}
else
{
// For iphone 4 or less
}
I never faced such an issue with any device as I've had one codebase for all, without any hardcoded values. What I do is to have the maximum sized image as resource instead of one for each device. For example, I would have one for retina display and show it as aspect fit so it will be views as is on every device.
Coming to deciding the frame of button, for instance, at run time. For this I use the % value of the patent view, example , if I want the width to be half of parent view take 50 % of parent and same applies for height and center.
With this I don't even need the xibs.
You can use this define to calculate if you are using the iPhone 5 based on screen size:
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
then use a simple if statement :
if (IS_IPHONE_5) {
// What ever changes
}
Peter, you should really take a look at Canappi, it does all that for you, all you have to do is specify the layout as such:
button mySubmitButton 'Sumbit' (100,100,100,30 + 0,88,0,0) { ... }
From there Canappi will generate the correct objective-c code that detects the device the app is running on and will use:
(100,100,100,30) for iPhone4
(100,**188**,100,30) for iPhone 5
Canappi works like Interface Builder and Story Board combined, except that it is in a textual form. If you already have XIB files, you can convert them so you don't have to recreate the entire UI from scratch.
You can manually check the screen size to determine which device you're on:
#define DEVICE_IS_IPHONE5 ([[UIScreen mainScreen] bounds].size.height == 568)
float height = DEVICE_IS_IPHONE5?568:480;
if (height == 568) {
// 4"
} else {
// 3"
}
You could add this code:
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone){
if ([[UIScreen mainScreen] respondsToSelector: #selector(scale)]) {
CGSize result = [[UIScreen mainScreen] bounds].size;
CGFloat scale = [UIScreen mainScreen].scale;
result = CGSizeMake(result.width * scale, result.height * scale);
if(result.height == 960) {
NSLog(#"iPhone 4 Resolution");
}
if(result.height == 1136) {
NSLog(#"iPhone 5 Resolution");
}
}
else{
NSLog(#"Standard Resolution");
}
}
This is a real universal code, you can create 3 different story board:
Set your project Universal mode, and set your main story iPhone with the iPhone5 storyboard and the ipad main with iPad target storyboard, now add new storyboard target for iphone and modify the resolution for iphone 4s or less now implement your AppDelegate.m
iPhone4/4s (is the same for 3/3Gs) one for iPhone5 and make the project universal, with a new Storyboard target for iPad, now in to AppDelegate.m under the didFinishLaunching add this code:
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone){
UIStoryboard *storyBoard;
CGSize result = [[UIScreen mainScreen] bounds].size;
CGFloat scale = [UIScreen mainScreen].scale;
result = CGSizeMake(result.width *scale, result.height *scale);
//----------------HERE WE SETUP FOR IPHONE4/4s/iPod----------------------
if(result.height == 960){
storyBoard = [UIStoryboard storyboardWithName:#"iPhone4_Storyboard" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
}
//----------------HERE WE SETUP FOR IPHONE3/3s/iPod----------------------
if(result.height == 480){
storyBoard = [UIStoryboard storyboardWithName:#"iPhone4_Storyboard" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
}
}
return YES;
}
So you have created a Universal app for iPhone 3/3Gs/4/4s/5 All gen of iPod, and All type of iPad
Remember to integrate all IMG with myImage.png and myImage#2x.png
According to me the best way of dealing with such problems and avoiding couple of condition required for checking the the height of device, is using the relative frame for views or any UI element which you are adding to you view for example: if you are adding some UI element which you want should at the bottom of view or just above tab bar then you should take the y origin with respect to your view's height or with respect to tab bar (if present) and we have auto resizing property as well. I hope this will work for you
Rather than using a set of conditionals you can resize your view automatically using the screen size.
int h = [[UIScreen mainScreen] bounds].size.height;
int w = [[UIScreen mainScreen] bounds].size.width;
self.imageView.frame = CGRectMake(20, 80, (h-200), (w-100));
In my case I want a view that fills the space between some input fields at the top and some buttons at the bottom, so fixed top left corner and variable bottom right based on screen size. My app fills the image view with the photo taken by the camera so I want all the space I can get.
If you need to convert an already existing app to universal, you need to select corresponding xib file->show Utilities-> Show Size inspector.
In Size inspector you can see Autosizing, by using this tool you can convert to existing iOS App.
Using xCode 5, select "Migrate to Asset Catalog" on Project>General.
Then use "Show in finder" to find your launch image, you can dummy-edit it to be 640x1136, then drag it into the asset catalog as shown in the image below.
Make sure that both iOS7 and iOS6 R4 section has an image that is 640x1136. Next time you launch the app, the black bars will disappear, and your app will use 4 inch screen
Point worth notice - in new Xcode you have to add this image file Default-568h#2x.png to assets
Use the Auto Layout feature for views. It will adjust automatically to all resolutions.
Create two xibs for a controller having controller name with suffix either ~iphone or ~ipad. At compile time, Xcode will take the right xib based on the device.
Use size classes, if you want to create a single xib for both iPhone and iPad, if the view is simple enough to port to iPhone and iPad.
There is a slight problem when testing on both iOS device and iOS Simulator. It appears that simulator (XCode 6.0.1) gives switched values for width and height in [[UIScreen mainScreen] bounds].size depending on a device orientation.
So this might be a problem when determinating the right physical screen size. This code helps also to distinct all 2014. iPhone model generations:
iPhone4s
iPhone5 (and iPhone5s)
iPhone6 (and iPhone6+)
It can also be easily changed to make the distinction between e.g. iPhone6 from iPhone6+.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
CGSize iOSDeviceScreenSize = [[UIScreen mainScreen] bounds].size;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
{
if (iOSDeviceScreenSize.width > 568 || // for iOS devices
iOSDeviceScreenSize.height > 568) // for iOS simulator
{ // iPhone 6 and iPhone 6+
// Instantiate a new storyboard object using the storyboard file named Storyboard_iPhone6
storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone6" bundle:nil];
NSLog(#"loaded iPhone6 Storyboard");
}
else if (iOSDeviceScreenSize.width == 568 || // for iOS devices
iOSDeviceScreenSize.height == 568) // for iOS simulator
{ // iPhone 5 and iPod Touch 5th generation: 4 inch screen (diagonally measured)
// Instantiate a new storyboard object using the storyboard file named Storyboard_iPhone5
storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone5" bundle:nil];
NSLog(#"loaded iPhone5 Storyboard");
}
else
{ // iPhone 3GS, 4, and 4S and iPod Touch 3rd and 4th generation: 3.5 inch screen (diagonally measured)
// Instantiate a new storyboard object using the storyboard file named Storyboard_iPhone4
storyboard = [UIStoryboard story boardWithName:#"MainStoryboard_iPhone" bundle:nil];
NSLog(#"loaded iPhone4 Storyboard");
}
}
else if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
{ // The iOS device = iPad
storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPadnew" bundle:nil];
NSLog(#"loaded iPad Storyboard");
}
// rest my code
}
I would suggest to use Autoresizing Mask in your applications according to your UI interface, it saves a lot of trouble and is better than making different UI for iPhone 4 and 5 screens.

Resources