gpt4 book ai didi

ios - 更改内部 View 的宽度,其中内部 View 的宽度取决于外部 View 。

转载 作者:行者123 更新时间:2023-11-30 11:03:36 26 4
gpt4 key购买 nike

如果有人能在这里指导我找到正确的解决方案,那就太棒了。这是我今天在这里的第二个问题,我很犹豫是否在这里问它,但我正在学习全新的东西(比如创建你自己的分段控件等),并且实际上不知道从哪里开始这个错误。我尝试用我有限的知识尽可能多地进行调试。

这是我的问题摘要。

我有两种看法。内部 View 的宽度取决于外部 View 的宽度。因此,如果我更新外部 View 的宽度约束(使用约束 IBoutlet),外部 View 的宽度会发生变化,但内部 View 的宽度仍与旧的相同。在更改外部 View 的宽度约束后,我在外部 View 上执行了layoutIfNeeded(),但没有任何反应。

详细信息:

我有一个分段控件(外部 View ),其选择器的宽度(内部 View )取决于分段控件的总宽度。正如我上面所说,在我更改分段控件的总宽度后,选择器的宽度保持不变。选择器的宽度取决于分段控件的宽度。

这是说明我的问题的图片。

enter image description here

正如您所看到的,我的选择器的宽度没有更新。它应该是新分段控件总宽度的一半。

我如何更新分段控件的宽度?

基本上,我将分段控件的宽度约束作为 VC 中的 IBOUtlet,然后根据屏幕尺寸增加了它的宽度。但选择器的宽度保持不变。

这是我用来更改分段控件宽度的代码

 DispatchQueue.main.async {
self.segmentedWidthControl.constant = UIScreen.main.bounds.width/2
self.segmentedControl.layoutIfNeeded()
//self.segmentedControl.updateConstraints() // this doesn't work either
}

对于自定义分段控件,我遵循了 youtube 上的教程。这是代码

@IBDesignable
class SegmentedControl: UIControl{

var buttons = [UIButton]()

var selector: UIView!

var selectSegmentIndex = 0

@IBInspectable
var borderWidth: CGFloat = 0{

didSet{
layer.borderWidth = borderWidth
}
}
@IBInspectable
var borderColor: UIColor = .clear {

didSet{
layer.borderColor = borderColor.cgColor
}
}

override func draw(_ rect: CGRect) {
layer.cornerRadius = frame.height/2
}

@IBInspectable
var commaSeperatedButtonTitles: String = ""{
didSet{

updateView()
}
}

@IBInspectable
var selectorColor: UIColor = .white{

didSet{
updateView()
}
}

@IBInspectable
var selectorTextColor: UIColor = .white{

didSet{
updateView()
}
}

@IBInspectable
var TextColor: UIColor = .lightGray {

didSet{
updateView()

}
}



func updateView(){

buttons.removeAll()

subviews.forEach { $0.removeFromSuperview()}

let buttonTitles = commaSeperatedButtonTitles.components(separatedBy: ",")
for buttonTitle in buttonTitles{
let button = UIButton(type: .system)
button.setTitle(buttonTitle, for: .normal)
button.setTitleColor(TextColor, for: .normal)
button.addTarget(self, action: #selector(buttonTapped(button: )), for: .touchUpInside )
buttons.append(button)
}

buttons[0].setTitleColor(selectorTextColor, for: .normal)


let selectorWidth = frame.width/CGFloat(buttonTitles.count)

selector = UIView(frame: CGRect(x: 0, y: 0, width: selectorWidth, height: frame.height))
selector.backgroundColor = selectorColor
selector.translatesAutoresizingMaskIntoConstraints = false
selector.layer.cornerRadius = frame.height/2

addSubview(selector)

let sv = UIStackView(arrangedSubviews: buttons)
sv.axis = .horizontal
sv.alignment = .fill
sv.translatesAutoresizingMaskIntoConstraints = false
sv.distribution = .fillEqually

addSubview(sv)
sv.topAnchor.constraint(equalTo: topAnchor).isActive = true
sv.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
sv.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
sv.leftAnchor.constraint(equalTo: leftAnchor).isActive = true



}

@objc func buttonTapped(button:UIButton){
for (buttonIndex,btn) in buttons.enumerated(){
btn.setTitleColor(TextColor, for: .normal)
if(btn == button){
selectSegmentIndex = buttonIndex
let selectorStartPosition = frame.width/CGFloat(buttons.count) * CGFloat(buttonIndex)
UIView.animate(withDuration: 0.3) {
self.selector.frame.origin.x = selectorStartPosition
}
btn.setTitleColor(selectorTextColor, for: .normal)
}
}

sendActions(for: .valueChanged)
}

}

如果您想运行该应用程序,请点击以下 GITHUB 链接。 https://github.com/Rikenm/Auto-Counter-iOS

最后感谢您的帮助。

最佳答案

我强烈建议使用约束和自动布局,而不是显式设置框架。

这是您的自定义类,实际上只做了一些更改。我已经评论了我所做的一切:

@IBDesignable
class SegmentedControl: UIControl{

var buttons = [UIButton]()

var selector: UIView!

var selectSegmentIndex = 0

// leading constraint for selector view
var selectorLeadingConstraint: NSLayoutConstraint!

@IBInspectable
var borderWidth: CGFloat = 0{

didSet{
layer.borderWidth = borderWidth
}
}
@IBInspectable
var borderColor: UIColor = .clear {

didSet{
layer.borderColor = borderColor.cgColor
}
}

override func draw(_ rect: CGRect) {
layer.cornerRadius = frame.height/2
}

@IBInspectable
var commaSeperatedButtonTitles: String = ""{
didSet{

updateView()
}
}

@IBInspectable
var selectorColor: UIColor = .white{

didSet{
updateView()
}
}

@IBInspectable
var selectorTextColor: UIColor = .white{

didSet{
updateView()
}
}

@IBInspectable
var TextColor: UIColor = .lightGray {

didSet{
updateView()

}
}

// this will update the control in IB
// when constraints are changed
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
updateView()
}

// this will keep the selector corners "round"
override func layoutSubviews() {
super.layoutSubviews()
selector.layer.cornerRadius = selector.frame.height / 2.0
}

func updateView(){

buttons.removeAll()

subviews.forEach { $0.removeFromSuperview()}

let buttonTitles = commaSeperatedButtonTitles.components(separatedBy: ",")
for buttonTitle in buttonTitles{
let button = UIButton(type: .system)
button.setTitle(buttonTitle, for: .normal)
button.setTitleColor(TextColor, for: .normal)
button.addTarget(self, action: #selector(buttonTapped(button: )), for: .touchUpInside )
buttons.append(button)
}

buttons[0].setTitleColor(selectorTextColor, for: .normal)

// not needed
//let selectorWidth = frame.width/CGFloat(buttonTitles.count)

// we're going to use auto-layout, so no need to set a frame
//selector = UIView(frame: CGRect(x: 0, y: 0, width: selectorWidth, height: frame.height))
selector = UIView(frame: CGRect.zero)

selector.backgroundColor = selectorColor
selector.translatesAutoresizingMaskIntoConstraints = false
selector.layer.cornerRadius = frame.height/2

addSubview(selector)

// constrain selector top to self.top
selector.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
// constrain selector height to self.height
selector.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true

// constrain selector width to self.width
// with multiplier of 1 / number of buttons
let m = 1.0 / CGFloat(buttons.count)
selector.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: m).isActive = true

// instantiate leading constraint for selector, and
// keep a reference in var selectorLeadingConstraint
selectorLeadingConstraint = selector.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0.0)

// make it active
selectorLeadingConstraint.isActive = true

let sv = UIStackView(arrangedSubviews: buttons)
sv.axis = .horizontal
sv.alignment = .fill
// sv.distribution = .fillProportionally
sv.translatesAutoresizingMaskIntoConstraints = false
sv.distribution = .fillEqually

addSubview(sv)
sv.topAnchor.constraint(equalTo: topAnchor).isActive = true
sv.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
sv.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
sv.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
}

@objc func buttonTapped(button:UIButton){
for (buttonIndex,btn) in buttons.enumerated(){
btn.setTitleColor(TextColor, for: .normal)
if(btn == button){
selectSegmentIndex = buttonIndex
let selectorStartPosition = frame.width/CGFloat(buttons.count) * CGFloat(buttonIndex)

// update selector's leading constraint, instead of explicit frame
//self.selector.frame.origin.x = selectorStartPosition
self.selectorLeadingConstraint.constant = selectorStartPosition

UIView.animate(withDuration: 0.3) {
self.layoutIfNeeded()
}
btn.setTitleColor(selectorTextColor, for: .normal)
}
}

sendActions(for: .valueChanged)
}

}
<小时/>

编辑:

这是另一种选择。不使用 UIStackView,而是使用约束来布局按钮。 “选择器” View 现在将成为按钮的同级 View ,因此您可以使用 centerX 约束,而不是计算 .leadingAnchor 约束。

最大的好处是,现在您可以在显示自定义分段控件后更改其大小,并且“选择器”大小和位置将自动更新。例如,如果将控件的宽度设置为屏幕(或其 super View )宽度的 50%,然后旋转设备以使控件变宽或变窄。

@IBDesignable
class SegmentedControl: UIControl{

var buttons = [UIButton]()

var selector: UIView!

// centerX constraint for selector view
var selectorCenterXConstraint: NSLayoutConstraint!

@IBInspectable
var borderWidth: CGFloat = 0{

didSet{
layer.borderWidth = borderWidth
}
}
@IBInspectable
var borderColor: UIColor = .clear {

didSet{
layer.borderColor = borderColor.cgColor
}
}

override func draw(_ rect: CGRect) {
layer.cornerRadius = frame.height/2
}

@IBInspectable
var commaSeperatedButtonTitles: String = ""{
didSet{

updateView()
}
}

@IBInspectable
var selectorColor: UIColor = .white{

didSet{
updateView()
}
}

@IBInspectable
var selectorTextColor: UIColor = .white{

didSet{
updateView()
}
}

@IBInspectable
var TextColor: UIColor = .lightGray {

didSet{
updateView()

}
}

// this will update the control in IB
// when constraints are changed
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
updateView()
}

// this will keep the selector corners "round"
override func layoutSubviews() {
super.layoutSubviews()
selector.layer.cornerRadius = selector.frame.height / 2.0
}

func updateView(){

buttons.removeAll()

subviews.forEach { $0.removeFromSuperview()}

// deactivate centerX constraint if its been initialized
if selectorCenterXConstraint != nil {
selectorCenterXConstraint.isActive = false
}

// add the selector view first
selector = UIView(frame: CGRect.zero)

selector.backgroundColor = selectorColor
selector.translatesAutoresizingMaskIntoConstraints = false
selector.layer.cornerRadius = frame.height/2

addSubview(selector)

let buttonTitles = commaSeperatedButtonTitles.components(separatedBy: ",")
for buttonTitle in buttonTitles{
let button = UIButton(type: .system)
button.setTitle(buttonTitle, for: .normal)
button.setTitleColor(TextColor, for: .normal)
button.addTarget(self, action: #selector(buttonTapped(button: )), for: .touchUpInside )
buttons.append(button)
}

buttons[0].setTitleColor(selectorTextColor, for: .normal)

// add each button and set top and height constraints
buttons.forEach {
self.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
$0.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
$0.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
}

// constrain first button's leading to self.leading
// constrain last button's trailing to self.trailing
NSLayoutConstraint.activate([
buttons[0].leadingAnchor.constraint(equalTo: self.leadingAnchor),
buttons[buttons.count - 1].trailingAnchor.constraint(equalTo: self.trailingAnchor),
])

// constrain each button's width to the first button's width
for i in 1..<buttons.count {
buttons[i].leadingAnchor.constraint(equalTo: buttons[i - 1].trailingAnchor).isActive = true
buttons[i].widthAnchor.constraint(equalTo: buttons[0].widthAnchor).isActive = true
}

// constrain selector top, height and width to first button's top, height and width
selector.topAnchor.constraint(equalTo: buttons[0].topAnchor).isActive = true
selector.heightAnchor.constraint(equalTo: buttons[0].heightAnchor).isActive = true
selector.widthAnchor.constraint(equalTo: buttons[0].widthAnchor).isActive = true

// constrain selector's centerX to first button's centerX
selectorCenterXConstraint = selector.centerXAnchor.constraint(equalTo: buttons[0].centerXAnchor)
selectorCenterXConstraint.isActive = true

}

@objc func buttonTapped(button:UIButton){
buttons.forEach { btn in
btn.setTitleColor(TextColor, for: .normal)
if (btn == button) {

// deactivate selector's current centerX constraint
self.selectorCenterXConstraint.isActive = false

// constrain selector's centerX to selected button's centerX
self.selectorCenterXConstraint = self.selector.centerXAnchor.constraint(equalTo: btn.centerXAnchor)

// re-activate selector's centerX constraint
self.selectorCenterXConstraint.isActive = true

UIView.animate(withDuration: 0.3) {
self.layoutIfNeeded()
}
btn.setTitleColor(selectorTextColor, for: .normal)
}
}

sendActions(for: .valueChanged)
}

}
<小时/>

在这里发布问题的一个技巧 --- 阅读 How to create a Minimal, Complete, and Verifiable Example

关于ios - 更改内部 View 的宽度,其中内部 View 的宽度取决于外部 View 。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52998870/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com