Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine the Today extension left margin properly in iOS 8?

I'm trying to find out how to calculate the left margin in the Today extension main view to align the contents to the rest of the Today view labels.

Here's an example with a clean Xcode project using a Today extensions (I've added color to the view backgrounds and drawn a dashed red line to illustrate where I'd like to align the Hello World UILabel).

The result in iPhone 6 Plus simulator (left side landscape, right side portrait) can be found from the image below:

enter image description here

In the image, notice that the green main view left boundary is placed differently related to the app name UILabel "testi2". It also seems that the red line - main views left border alignment is different in each device: iPhone 5x, iPhone 6 and iPads.

The behavior can be reproduced using a clean Xcode project (I'm using Xcode 6.1.1, iOS 8.1 and Swift):

  1. Create an empty Xcode project (A single-view application)
  2. Add a new Target: Extensions > Today extension
  3. From the Today extension group, find MainInterface.storyboard and make the main view background green and Hello world UILabel background red: enter image description here

How do I align the the Hello World UILabel (red background) to the dashed line? Or how do I align the main view (green background) to the dashed line?

like image 504
Markus Rautopuro Avatar asked Dec 28 '14 17:12

Markus Rautopuro


3 Answers

Did you try this one ?

Objective-C:

- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets
{
    return UIEdgeInsetsZero;
}

Swift:

func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets {
        return UIEdgeInsetsZero
}

Otherwise it looks like you will have to set them manually according to this So-Thread:

EDIT:

Looks like this method is deprecated and is not called for devices running >= iOS10. However I could not find any documentation on an alternative. If you have any information on this please add to this post so everyone can profit. Just make sure when using this function that it will not be called on >= iOS10.

Source: Apple Documentation

like image 110
dehlen Avatar answered Nov 02 '22 09:11

dehlen


I tried using the left value of the defaultMarginInset of -widgetMarginInsetsForProposedMarginInsets with mixed results: On the iPhone 5S screen dimensions, it can be used to get the same inset as the default calendar widget of iOS (note that the right margin of the time label aligns with the blue line):

5S Portrait

5S Landscape

On the iPhone 6, you get similar results, i.e. it also aligns. However, on iPhone 6 Plus, the calendar widget somehow scales the inset:

6P Portrait

6P Landscape

Note that in the landscape version, neither the time nor the lines align to anything.

In conclusion, I would say that you can safely use defaultMarginInset.left to get decent results.


Swift code:

class TodayViewController: UIViewController, NCWidgetProviding {

    var defaultLeftInset: CGFloat = 0
    var marginIndicator = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()

        marginIndicator.backgroundColor = UIColor.whiteColor()
        view.addSubview(marginIndicator)
    }

    func widgetMarginInsetsForProposedMarginInsets(var defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets {
        defaultLeftInset = defaultMarginInsets.left

        defaultMarginInsets.left = 0
        return defaultMarginInsets
    }

    func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
        marginIndicator.frame = CGRectMake(defaultLeftInset, 0, 10, view.frame.size.height)
        completionHandler(NCUpdateResult.NewData)
    }
}
like image 35
fabian789 Avatar answered Nov 02 '22 08:11

fabian789


My temporary solution goes as follows. I'm using constant values for setting the left and top margins. This way I can align the content exactly like it's, for example, in Tomorrow Summary widget.

First some helper methods for determining the device type (adapted from this answer):

struct ScreenSize {
    static let SCREEN_WIDTH = UIScreen.mainScreen().bounds.size.width
    static let SCREEN_HEIGHT = UIScreen.mainScreen().bounds.size.height
    static let SCREEN_MAX_LENGTH = max(ScreenSize.SCREEN_WIDTH,
        ScreenSize.SCREEN_HEIGHT)
    static let SCREEN_MIN_LENGTH = min(ScreenSize.SCREEN_WIDTH,
        ScreenSize.SCREEN_HEIGHT)
}

struct DeviceType {
    static let iPhone4 =  UIDevice.currentDevice().userInterfaceIdiom == .Phone
        && ScreenSize.SCREEN_MAX_LENGTH < 568.0
    static let iPhone5 = UIDevice.currentDevice().userInterfaceIdiom == .Phone
        && ScreenSize.SCREEN_MAX_LENGTH == 568.0
    static let iPhone6 = UIDevice.currentDevice().userInterfaceIdiom == .Phone
        && ScreenSize.SCREEN_MAX_LENGTH == 667.0
    static let iPhone6Plus = UIDevice.currentDevice().userInterfaceIdiom == .Phone
        && ScreenSize.SCREEN_MAX_LENGTH == 736.0
    static let iPad = UIDevice.currentDevice().userInterfaceIdiom == .Pad
}

Then using widgetMarginInsetsForProposedMarginInsets: as suggested I overwrite the left and top insets as follows:

func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets)
        -> UIEdgeInsets {
    var insets = defaultMarginInsets
    let isPortrait = UIScreen.mainScreen().bounds.size.width
        < UIScreen.mainScreen().bounds.size.height

    insets.top = 10.0
    if DeviceType.iPhone6Plus {
        insets.left = isPortrait ? 53.0 : 82.0
    } else if DeviceType.iPhone6 {
        insets.left = 49.0
    } else if DeviceType.iPhone5 {
        insets.left = 49.0
    } else if DeviceType.iPhone4 {
        insets.left = 49.0
    } else if DeviceType.iPad {
        insets.left = isPortrait ? 58.0 : 58.0
    }

    return insets
}

However, this is not the solution I'm looking for - I'd like to get rid of hardcoding per-device-per-orientation pixel values.

like image 4
Markus Rautopuro Avatar answered Nov 02 '22 10:11

Markus Rautopuro