12

I know we are operating on points not pixels and in most cases it's convenient, but I need to make UIView be 1 pixel instead of 2 pixel height. So, if you drag and drop some UIView (separator line) in Interaface builder, and make it the height of 1px (point) then it will still look like 2 pixel size line on retina screen (both on device and simulator).

I know there contentScaleFactor property on the view which show is it retina (2.0f) or not (1.0f). It looks like the views has the value of 1.0f, so you need to retrieve that from main screen:

[UIScreen mainScreen].scale; 

This returns me 2.0f. Now, I'v added height constraint for this separator view added the method which checks isRetina and divides the line to make it exactly 1 pixel:

- (void)awakeFromNib{

  [super awakeFromNib];

  CGFloat isRetina = ([UIScreen mainScreen].scale == 2.0f) ? YES : NO;

  if (isRetina) {

    self.separatorViewHeightConstraint.constant /= 2;
  }
}

This works, I'm just not sure is it good idea to use 0.5 value ...

Centurion
  • 14,106
  • 31
  • 105
  • 197

4 Answers4

12

To support newer 3x displays (e.g. iPhone 6+) use this code:

UIScreen* mainScreen = [UIScreen mainScreen];
CGFloat onePixel = 1.0 / mainScreen.scale;
if ([mainScreen respondsToSelector:@selector(nativeScale)])
    onePixel = 1.0 / mainScreen.nativeScale;
Alejandro
  • 3,726
  • 1
  • 23
  • 29
  • Can confirm this works for 2x and 3x devices to create true '1 pixels'. Thanks. – micnguyen Apr 23 '15 at 05:17
  • 1
    This does not work on iPhone 6 Plus I'm afraid, as the screen is rendered in 1242x2208 and then down sampled to 1080x1920. sometimes you will get an almost perfect 1px line, and sometimes the line will disappear completely. http://www.paintcodeapp.com/news/iphone-6-screens-demystified – Stefan Fisk Apr 24 '15 at 14:05
  • Has this method been tested on an actual iPhone 6 Plus? – blwinters Dec 19 '15 at 14:28
10

Your code is valid. Using 0.5 to set the frame of a UIView will work as desired, as the frame's arguments are CGFloat's. If you wish to use a CGFloat representing a single pixel in point units for something other than self.separatorViewHeightConstraint.constant, the code below will work.

CGFloat scaleOfMainScreen = [UIScreen mainScreen].scale;
CGFloat alwaysOnePixelInPointUnits = 1.0/scaleOfMainScreen;
Matthew
  • 541
  • 3
  • 9
5

You could just do

self.separatorViewHeightConstraint.constant = self.separatorViewHeightConstraint.constant / [UIScreen mainScreen].scale;

yes setting the value to 0.5 is the only way to get "real" 1px lines on retina

rist
  • 578
  • 2
  • 9
2

Sadly none of the other answers apply for iPhone 6 Plus.

1px lines are not possible on iPhone 6 Plus, as the screen is rendered in 1242x2208 and then down sampled to 1080x1920. Sometimes you will get an almost perfect 1px line, and sometimes the line will disappear completely.

See http://www.paintcodeapp.com/news/iphone-6-screens-demystified for a proper explanation.

Stefan Fisk
  • 1,563
  • 13
  • 19
  • There is a way to do it: http://stackoverflow.com/questions/23666209/how-do-i-create-a-1px-line-in-interface-builder – Quentin Aug 22 '15 at 06:43
  • @Quentin which answer are you referring to? when skimming through them I only saw different version of using the screen scale factor, and as described in detail in my link that cannot ever work, although sometimes the results although not perfect might be acceptable. – Stefan Fisk Aug 24 '15 at 07:00
  • 1
    I successfully implemented the NSLayoutConstraint subclass technic ```@implementation KBHairlineLayoutConstraint #pragma mark - - (void)awakeFromNib { [super awakeFromNib]; if ( self.constant == 1 ) self.constant = 1/[UIScreen mainScreen].scale; } @end``` – Quentin Aug 24 '15 at 23:06
  • As is explained in the linked article that method does not work on 6+, and there is no way around that fact. The result might be good enough for what you are doing, but if so that is by sheer luck as the lines will start looking seriously weird if laid out such that they get antialiased differently. – Stefan Fisk Aug 25 '15 at 14:20
  • Actually, I have only tested on simulator. :( – Quentin Aug 25 '15 at 18:11
  • ah, for 6+ the simulator is sadly not reliable when doing this stuff. it seems like it doesn't do the scaling the same way as the device does, and therefore the problem won't be visible. I was doing the same thing until a tester started complaining and I got myself an actual device to test on. – Stefan Fisk Aug 25 '15 at 18:52