ios 约束未被停用

z2acfund  于 2023-05-19  发布在  iOS
关注(0)|答案(3)|浏览(147)

我正在练习自动布局编程。我想把一个UIView在控制器的宽度将是4/5在纵向模式,但当它将进入横向模式,我需要的高度是4/5的超级视图的高度,而不是宽度的控制器中心。
就像-

因此,我根据方向停用然后激活所需的约束,但当我更改旋转时,它会给我冲突,就好像它没有停用我指定要停用的约束一样。这是我的完整代码。因为它是独立于故事板的,你可以把视图控制器类分配给一个视图控制器,然后看到效果。

class MyViewController: UIViewController {
    
    var widthSizeClass = UIUserInterfaceSizeClass.unspecified
    
    var centeredView : UIView = {
        let view = UIView()
        view.backgroundColor = UIColor.systemGreen
        return view
    }()
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.view.addSubview(centeredView)
        centeredView.translatesAutoresizingMaskIntoConstraints = false
    }
    
    override func viewWillLayoutSubviews(){
        super.viewWillLayoutSubviews()
        widthSizeClass = self.traitCollection.horizontalSizeClass
        addConstrainsToCenterView()
    }
    
    func addConstrainsToCenterView() {
        
        centeredView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
        centeredView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor).isActive = true
       
        let compactWidthAnchor = centeredView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 4/5)
        let compactHeightAnchor = centeredView.heightAnchor.constraint(equalTo: centeredView.widthAnchor)
       
        
        let regularHeightAnchor = centeredView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 4/5)
        let regularWidthAnchor = centeredView.widthAnchor.constraint(equalTo: centeredView.heightAnchor)
        
        if widthSizeClass == .compact{
            NSLayoutConstraint.deactivate([regularWidthAnchor, regularHeightAnchor])
            NSLayoutConstraint.activate([compactWidthAnchor, compactHeightAnchor])
        }
        else{
            NSLayoutConstraint.deactivate([compactWidthAnchor, compactHeightAnchor])
            NSLayoutConstraint.activate([regularWidthAnchor, regularHeightAnchor])
        }
    }
}

谁能帮我找出我的缺点。

jobtbby3

jobtbby31#

有几个问题...
1 -许多iPhone型号 * 只有 * 有wC hR(纵向)和wC hC(横向)尺寸类别。因此,如果您在这些设备上检查.horizontalSizeClass,它将始终是.compact。您可能需要检查.verticalSizeClass
2 -你有你的代码的方式,你正在创建***新***约束每次你调用addConstrainsToCenterView()。您没有 * 激活/停用 * 现有约束。
看看这个:

class MyViewController: UIViewController {
    
    var heightSizeClass = UIUserInterfaceSizeClass.unspecified

    var centeredView : UIView = {
        let view = UIView()
        view.backgroundColor = UIColor.systemGreen
        return view
    }()

    // constraints to activate/deactivate
    var compactAnchor: NSLayoutConstraint!
    var regularAnchor: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.addSubview(centeredView)
        centeredView.translatesAutoresizingMaskIntoConstraints = false

        // centeredView is Always centerX and centerY
        centeredView.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true
        centeredView.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor).isActive = true
        
        // for a square (1:1 ratio) view, it doesn't matter whether we set
        //  height == width
        // or
        //  width == height
        // so we can set this Active all the time
        centeredView.heightAnchor.constraint(equalTo: centeredView.widthAnchor).isActive = true
        
        // create constraints to activate / deactivate
        
        // for regular height, set the width to 4/5ths the width of the view
        regularAnchor = centeredView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 4/5)

        // for compact height, set the height to 4/5ths the height of the view
        compactAnchor = centeredView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 4/5)
        
    }

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        
        // use .verticalSizeClass
        heightSizeClass = self.traitCollection.verticalSizeClass

        updateCenterViewConstraints()
    }
    
    func updateCenterViewConstraints() {
        
        if heightSizeClass == .compact {
            // if height is compact
            regularAnchor.isActive = false
            compactAnchor.isActive = true
        }
        else{
            // height is regular
            compactAnchor.isActive = false
            regularAnchor.isActive = true
        }
    }
}

通过这种方法,我们为我们想要激活/停用的约束创建了两个变量:

// constraints to activate/deactivate
    var compactAnchor: NSLayoutConstraint!
    var regularAnchor: NSLayoutConstraint!

然后,在viewDidLoad()中,我们将centeredView添加到视图中,设置其“不变”约束- centerX、centerY、aspect-ratio -并创建两个激活/停用约束。
当我们改变size类时,我们只需要处理两个var约束。

5jdjgkvh

5jdjgkvh2#

可能不是答案,但为了沿着我的评论,下面是我成功使用的代码:

var p = [NSLayoutConstraint]()
var l = [NSLayoutConstraint]()

注意,pl是数组,分别代表纵向和横向。

override func viewDidLoad() {
    super.viewDidLoad()
    setupConstraints()
}

这里没什么特别的,只是说明了在加载视图时可以设置约束。

func setupConstraints() {        

    // for constraints that do not change, set `isActive = true`
    // for constants that do change, use `p.append` and `l.append`
    // for instance:

    btnLibrary.widthAnchor.constraint(equalToConstant: 100.0).isActive = true         

    p.append(btnLibrary.topAnchor.constraint(equalTo: safeAreaView.topAnchor, constant: 10))
    l.append(btnLibrary.bottomAnchor.constraint(equalTo: btnCamera.topAnchor, constant: -10))

再一次,这里没有什么-看起来你正在做这件事。下面是我在你的视图控制器覆盖中看到的区别:

var initialOrientation = true
var isInPortrait = false

override func viewWillLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if initialOrientation {
        initialOrientation = false
        if view.frame.width > view.frame.height {
            isInPortrait = false
        } else {
            isInPortrait = true
        }
        view.setOrientation(p, l)
    } else {
        if view.orientationHasChanged(&isInPortrait) {
            view.setOrientation(p, l)
        }
    }
}

public func orientationHasChanged(_ isInPortrait:inout Bool) -> Bool {
    if self.frame.width > self.frame.height {
        if isInPortrait {
            isInPortrait = false
            return true
        }
    } else {
        if !isInPortrait {
            isInPortrait = true
            return true
        }
    }
    return false
}
public func setOrientation(_ p:[NSLayoutConstraint], _ l:[NSLayoutConstraint]) {
    NSLayoutConstraint.deactivate(l)
    NSLayoutConstraint.deactivate(p)
    if self.bounds.width > self.bounds.height {
        NSLayoutConstraint.activate(l)
    } else {
        NSLayoutConstraint.activate(p)
    }
}

其中一些可能是矫枉过正为您使用。但是,我实际上不是检查类的大小,而是检查边界,沿着检测初始方向。给我用?我实际上设置了一个侧边栏或底部栏。适用于所有iPhone和iPad。
同样,我没有看到任何主要的-激活/停用一个名为(?)约束的数组而不是创建数组,这样做的顺序,你正在使用的覆盖...对我来说,最突出的一点就是看班级的大小。(可能会发现初始大小类是什么?))
我目前正在记录UISplitViewController如何决定显示Secondary或Compact VC。事实证明,它在至少 * 五个 * 组中的表现不同- iPad(始终是次要的),iPad分屏(除了iPad Pro 12.9外,所有iPad都是紧凑的),iPhone纵向(始终是紧凑的),最后,iPhone横向(大多数是紧凑的,但次要的是(iPhone 8 Plus,iPhone 11,iPhone 11 Pro Max和iPhone 12 Pro Max)。
注意:它是iPhone 11 Pro,iPhone 12和iPhone 12 Pro的紧凑型!我对此感到惊讶。(接下来我将直接测试大小类。)
我的观点?也许你需要去屏幕边界,以确定你想要什么布局,而不是大小类。这比班级规模更受你的控制。不管怎样,祝你好运!

ac1kyiln

ac1kyiln3#

很简单每次布局发生,每次你说例如:

let regularWidthAnchor = centeredView.widthAnchor.constraint(equalTo: centeredView.heightAnchor)
NSLayoutConstraint.deactivate([regularWidthAnchor, regularHeightAnchor])

您没有取消激活接口中现有的活动约束。您正在创建一个全新的约束,然后将其禁用(这是毫无意义的,因为它从未处于活动状态),然后将其丢弃。
您只需要创建这些约束一次,并保留对它们的引用。

相关问题