41

In iOS 7, the table view's separator line is thinner than previous iOS, and it looks like its 0.5 px width.

I need to split a cell in 2, with a similar separator line (only vertically) and I want this line to be the same width as the regular separator line.

So I'm wondering what is the best way to add such a line?

If I use a UIView and set its width to 0.5 it won't be visible, and if I set its width to 1.0, then of course I will get a line width of 1.0px not 0.5px.

I tried to use a resource with a retina size of 1.0px but it didn't work either

Shark Lasers
  • 441
  • 6
  • 15
Eyal
  • 10,777
  • 18
  • 78
  • 130

15 Answers15

29

You set the width as 0.5 - or to make it always 1 pixel then set it to (1.0 / [UIScreen mainScreen].scale)

I've done this before and it's worked fine

I think the reason you can't see it is that you are centering the line exactly in the middle of the screen, which will make the line's origin (320 - 0.5) / 2.0, being an X origin of 159.75 - the 0.75 may be messing things up - or the cell is removing the background color of your view

SomeGuy
  • 9,670
  • 3
  • 32
  • 35
  • This doesn't work on iPhone Plus models because the `nativeScale` is less than the `scale`, but it does work on the iPhone X since both are 3.0. – blwinters Nov 09 '17 at 16:23
27

I did this steps:

  1. Create a constraint with 1px height
  2. Connect the constraint with IBOutlet (file .m)
  3. Change the constant of the constraint for 0.5 in the ViewDidLoad

It works for me.

anthonyqz
  • 427
  • 4
  • 5
  • Works correctly, except of that I had to add it to the viewDidAppear instead of viewDidLoad – lonlywolf May 22 '15 at 07:46
  • 2
    Yes, because you must wait for create all constraint and then update it. – anthonyqz May 24 '15 at 03:45
  • 1
    you can call at viewDidLayoutSubviews for early start – padam thapa Nov 20 '15 at 10:38
  • 3
    When you set 0.5 pt like that, what happens to \@1x- and \@3x-devices? It will try to show 0.5px for \@1x (non-retina), which is impossible, and 1.5px for iPhone Plus-models, which is also impossible.. Does it round up to 1px and 2px, or down to 0px and 1px? Will there be anti-aliasing-problems? e.g glitchy/lagging/flashing lines? – Sti Oct 10 '16 at 11:49
15

Assuming you do your cell layouts with Storyboards:

Place an UIView with your desired height and 1px width into your cell. Give it whatever background color you'll like it to have. The standard separator color is approx. RGB(200,199,204).

Then create a subclass of UIView, e.g. HairlineView and override awakeFromNib()

-(void)awakeFromNib {
    self.layer.borderColor = [self.backgroundColor CGColor];
    self.layer.borderWidth = (1.0 / [UIScreen mainScreen].scale) / 2;

    self.backgroundColor = [UIColor clearColor];
}

You're drawing a border around your 1px wide view, which is 2 * 0.25px wide making it 0.5px in total, which is the width you wanted to achieve, then hide the background of the view by setting the background to clear. This last step is important, it won't work without.

Set the type of your view to HairlineView, then it should work.

When doing it programmatically, you'd have to override a different method, e.g. initWithFrame() maybe, since awakeFromNib() only gets called when you create the view from a Storyboard layout.

Irina Anastasiu
  • 658
  • 6
  • 8
  • This works almost perfectly. Except at the beginning of the view, the line thickness is 1.0 for 1 pixel then it drops to .5 thickness for the rest of the view – Todd Anderson Jan 30 '15 at 01:50
  • Work on 1x and 2x but not 3x. – John Pang Mar 22 '17 at 10:02
  • 5
    There is no need to divide 1.0 width by 2. This will make the color of line lighter than expected. On iOS 8 and above, the correct way is to use `= (1.0 / [UIScreen mainScreen].nativeScale)`. This will give: 1pt (and 1px) for 1x device, 0.5pt (and 1px) for 2x device, 0.38pt (and 1px) for 3x downsampling device (6 plus, 7 plus...) and 0.33 pt (and 1px) for 3x device (iPhone X). – John Pang Nov 24 '17 at 19:21
  • I created an IBInspectable HairlineView as mentioned. And I found the best way to use it is: add 3 side align constraint to its superview. If it serves as divider between two views, the two views should be adjacent to each other, and HairlineView is on top of them. So the views won't be moved by 0.5pt. – John Pang Nov 24 '17 at 19:26
  • This is very wrong. Borders are placed on the outside. If you use this solution what you will end up having is 2 very thing lines (0.25px or 0.16px) being separated by (1 - 2*line_thickness) clear space. See my answer below for easy way to solve this. – Raimundas Sakalauskas Aug 07 '19 at 14:17
11

Some of the answers above are already right, but just an update: in Xcode 8, now you can set constant of layout constraint with a decimal number. No longer need to connect IBOutlet or set constant via User Defined Runtime Attributes.

Decimal constant

kientux
  • 1,782
  • 1
  • 17
  • 32
  • 2
    worked for me for some time ago, but now it rounds it to zero (xCode 8.3.3) – Vladyslav Zavalykhatko Aug 24 '17 at 11:29
  • This is incorrect, because for @3x devices you want to have that as 0.33(3). See my answer below. – Raimundas Sakalauskas Aug 07 '19 at 14:15
  • @RaimundasSakalauskas my answer is correct if what OP means is "0.5 point" because, "0.5 pixel" is just impossible. – kientux Aug 07 '19 at 15:18
  • 0.5px is possible and it will be rendered by iOS because of antialiasing that is being applied to the view. Though I would probably bet that what they meant is 1px (to match the thickness of TableViewCell splitter). Your answer would be correct for 2x screens then, but it is not correct in general, because so many devices nowadays have 3x scale. – Raimundas Sakalauskas Aug 07 '19 at 16:30
11

You can set constant of layout constraint with a decimal number, but UIBuilder have a some bug in Xcode 8,9,10

My solution:

  1. Open xib file by source code enter image description here]
  2. Find your constant of layout constraint and change to 0.5 enter image description here
Andrey Dyatkov
  • 127
  • 1
  • 6
3

you can set constraint in storyboard height 0.5f; it won't be visible directly however it is visible under list of of constraints as shown in screenshot enter image description here

Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
Rabiea
  • 31
  • 1
  • 3
    On 3x screen, sometimes it is 2px tall, sometimes it is 1px tall. – John Pang Mar 22 '17 at 10:26
  • 1
    I created a HairlineView like answer by Irina and put the following code in awakeFromNib to change the constraint. - (void)awakeFromNib { [super awakeFromNib]; for (NSLayoutConstraint *constraint in self.constraints) { if (constraint.constant == 0.5f) { constraint.constant = (1.0 / [UIScreen mainScreen].scale); } } } – John Pang Mar 22 '17 at 10:37
2

You can use Core Graphics to draw the gradient:

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 1.0f)];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor blackColor] CGColor], nil];
[view.layer insertSublayer:gradient atIndex:0];
Mihriban Minaz
  • 3,043
  • 2
  • 32
  • 52
2

My approach:

public extension UIView {
    
    func thinDivider() {
        for c in constraints {
            if c.constant == 1 {
                c.constant = (1.0 / UIScreen.main.scale)
            }
        }
    }
    
}

Use wisely (only on dividers).

Note: simply set height constraint to 1 and call view.thinDivider() from code. This is lazy solution to avoid make outlets to every height constraint you need.

Sidenote: settings height to 0.5 in storyboard editor resets to 0 after build and view becomes invisible

Makalele
  • 7,431
  • 5
  • 54
  • 81
1

my solution:

  1. create a view, set a constraint the view: "height = 0px", go to the "Identity Inspector" and edit the priority of this constraint to 750.

  2. then select the view and click the "pin" button, add another constraint to this view: "height = 0.5" with priority of 1000. in the "Identity Inspector" you can see the second constraint automatically becomes "height >= 0.5"

works in Xcode 7.3.1

bubuStar
  • 563
  • 1
  • 3
  • 8
1

SwiftUI Use pixelLength to get the screen's pixels size which is the minimum line width on any screen and scale.

struct ContentView: View {
    @Environment(\.pixelLength) var pixelLength: CGFloat

    var body: some View {
        Rectangle()
            .foregroundColor(.clear)
            .border(Color.black, width: pixelLength)
     }
}
Norman
  • 3,020
  • 22
  • 21
0

You Put view in you cell at the bottom. and set its to dark color of the view. and also set its height 0.5 PX. you can got separator of each cell in it.keep its width according to cell width.

Kirit Modi
  • 23,155
  • 15
  • 89
  • 112
0

instead of trying to set your view height according to separator style.Remove the separator. set cell separator style to StyleNone.
Then add two view with height of .5 or 1 pixel at middle and bottom of cell.Now it looks both same and having a separator style.

santhu
  • 4,796
  • 1
  • 21
  • 29
0

Tricky way:

  1. In storyboard or xib, set a unique value (e.g. 123) to the line height constraint
  2. Open the storyboard or xib with text editor
  3. Replace the unique value (e.g. 123) with 0.5
mishimay
  • 4,237
  • 1
  • 27
  • 23
0

I use this class to draw 1 pixel line (not 1 dot, but one pixel).

class HairlineView: UIView {
    override func awakeFromNib() {
        super.awakeFromNib()

        for constraint in self.constraints {
            if let _ = constraint.firstItem as? HairlineView,
               constraint.firstAttribute == .height,
               constraint.firstAnchor.isKind(of: NSLayoutDimension.self),
               constraint.secondItem == nil,
               constraint.secondAnchor == nil {
                constraint.constant = (1.0 / UIScreen.main.scale)
                return
            }
        }
    }
}

Prerequisite for this is to have UIView with height constraint attached. Code will look through existing constraints for the view, find the height constraint and set its value to your_desired_height in pixels (don't confuse with dots).

Code can easily be changed to any value by replacing your_desired_height to whatever value you need.

Raimundas Sakalauskas
  • 2,016
  • 21
  • 24
-1

You can solve this issue in the following way in Xcode 8 / iOS 10, without using constraints:

  1. Add a UIView in your storyboard using Interface Builder. Set the properties for this as you see fit, just to have a visual representation of how it would look in your cell (i.e., height = 44, width = 1). At this point it does not matter if the width is larger than the one you need (i.e. 0.5).

  2. Create an IBOutlet for this view in your view controller (i.e., UIView *splitLine).

  3. In the viewWillAppear: method of your view controller add the following method:

[self.splitLine setFrame:CGRectMake( x, y, 0.5, 44)];

In this method you specify the width of your separating line to 0.5, and the height to whatever values you want. These values overwrite the values specified in the storyboard.