使用自动布局的基于百分比的保证金

时间:2014-11-28 09:38:38

标签: ios autolayout

我正在尝试使用基于父视图宽度的左边距放置我的子视图。这听起来很简单,但我无法弄清楚如何使用autolayout来做到这一点。

逻辑上,我只需要将左边距值设置为父宽度的某个百分比值,但此刻,我无法将该逻辑转换为自动布局。

这是我目前的代码:

var view = UIView();
view.backgroundColor = UIColor.redColor();
view.frame = CGRectMake(0, 0, 320, 400);

var sview = UIView();
sview.setTranslatesAutoresizingMaskIntoConstraints(false);
sview.backgroundColor = UIColor.yellowColor();
//sview.frame = CGRectMake(0, 0, 50, 50);
view.addSubview(sview);

var dict = Dictionary<String, UIView>()
dict["box"] = sview;

var con1 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 20.0);
view.addConstraint(con1);

var con2 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 0.75, constant: 0.0);
view.addConstraint(con2);

var con3 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0);
view.addConstraint(con3);

var con4 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0.0);
view.addConstraint(con4);

这是代码返回错误的地方:

var con2 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 0.75, constant: 0.0);
view.addConstraint(con2);

错误:

  

*由于未捕获的异常'NSInvalidArgumentException'而终止应用,原因:'* + [NSLayoutConstraint   constraintWithItem:属性:relatedBy:toItem:属性:乘数:常数:]:   布局属性的配对无效

有没有人知道如何实现这个目标?我只想让左边距为父视图宽度的0.75%。

感谢。

4 个答案:

答案 0 :(得分:8)

你想要的是sview 位于view 的某个点并且您写道,您希望sview的左侧位于view宽度的某个点,这不是您的错误所说的布局属性的正确配对。

以下是您需要做的事情:

NSLayoutConstraint(item: sview, 
              attribute: NSLayoutAttribute.Left, 
              relatedBy: NSLayoutRelation.Equal, 
                 toItem: view, 
              attribute: NSLayoutAttribute.Left, 
             multiplier: 1, 
               constant: (CGRectGetWidth(view.bounds) * 0.75));

希望它有所帮助!

修改

我发现了一篇关于基于Percented的边距的精彩文章:
http://simblestudios.com/blog/development/percentage-width-in-autolayout.html
甚至更简单:
http://simblestudios.com/blog/development/easier-percentage-width-in-autolayout.html

enter image description here

答案 1 :(得分:5)

可以在子视图及其超级视图之间创建具有自动布局约束的基于百分比的边距。随着超视图的大小变化,边距将动态变化。例如,这是如何创建10%的追踪保证金。

  1. 在视图及其超级视图之间创建一个尾随约束。
  2. 确保第一个约束项是子视图,第二个项是superview。这可以通过单击第一个项目下拉菜单并选择反向第一和第二项来完成。
  3. 在属性检查器中将约束的常量值更改为零。
  4. 将乘数值更改为0.9。
  5. enter image description here

    这是手动方法的一个问题 - 例如,它不适用于从右到左的语言,阿拉伯语。从右到左的布局需要对约束进行一些不同的设置,但我们不能将它们保留在一个故事板中。

    这是我编写的一个库,可以让您创建基于百分比的约束。它确实处理了从右到左的语言案例。

    https://github.com/exchangegroup/PercentageMargin

答案 2 :(得分:1)

您可以通过NSLayoutConstraint@IBInspectable子类化为接受保证金百分比。然后订阅UIDeviceOrientationDidChangeNotification以运行计算并填入constant值,以便在布局更改时更新。

/// Layout constraint to calculate size based on multiplier.
class PercentLayoutConstraint: NSLayoutConstraint {

    @IBInspectable var marginPercent: CGFloat = 0

    var screenSize: (width: CGFloat, height: CGFloat) {
        return (UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        guard marginPercent > 0 else { return }
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: #selector(layoutDidChange),
            name: UIDeviceOrientationDidChangeNotification,
            object: nil)
    }

    /**
     Re-calculate constant based on orientation and percentage.
     */
    func layoutDidChange() {
        guard marginPercent > 0 else { return }

        switch firstAttribute {
        case .Top, .TopMargin, .Bottom, .BottomMargin:
            constant = screenSize.height * marginPercent
        case .Leading, .LeadingMargin, .Trailing, .TrailingMargin:
            constant = screenSize.width * marginPercent
        default: break
        }
    }

    deinit {
        guard marginPercent > 0 else { return }
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

首先在Identity Inspector中指定新的子类: enter image description here

然后你可以像这样使用它: enter image description here

我能想到的唯一警告是故事板中的常量不会在运行时使用,而是会被基于百分比的计算覆盖。所以它确实需要一些重复的工作,一次是基于点在Storyboard上实际布局视图,这样你就可以了解屏幕布局的样子,然后在运行时启动百分比。

有关详细信息,请查看此文章:http://basememara.com/percentage-based-margin-using-autolayout-storyboard/

答案 3 :(得分:0)

除了凯文回答,您还需要为方框超级视图添加约束。

var view = UIView();
    view.backgroundColor = UIColor.redColor();
    view.setTranslatesAutoresizingMaskIntoConstraints(false);
    self.view.addSubview(view);
    var viewObject = ["mainview" : self.view , "view" : view];
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[view(==320)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewObject));
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[view(==400)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewObject));