是否有与 SwiftUI 的 zstack 等效的 UIKit?
is there a UIKit equivalent to SwiftUI's zstack?
我正在尝试创建类似 this 的内容。我最近一直在使用 SwiftUI,所以我知道我可以通过向 zstack 添加图像、文本和按钮(我很灵活的文本是 button/NavigationLink 的标签)来创建它。但我环顾四周,想看看 UIKit 是否有办法做到这一点。最好不使用故事板。我对 cocoapods 库或任何需要的东西持开放态度。我环顾四周,探索使用 SwiftUI 创建所需的 ZStack,然后在我的 UIKit 中使用它和 UIHostingController,但因为它涉及 button/navigationlink。看到 NavigationLink 如何要求目的地符合视图,我想在将我的更多项目转换为 swiftui 之前四处询问。我更希望这个项目能给我更多在没有故事板的情况下在 UIKit 中构建视图的经验,所以我更愿意这样做而不是使用 SwiftUI。如果可能的话我想。
我尝试四处搜索,但我所有 google 涉及 UIButton 和图像的搜索都只是 link 到关于在 UIButton 中设置图像的帖子。
由于您想获得更多使用 UIKit
创建视图的经验,我创建了一个继承自 UIView
的视图,您可以重复使用。在 UIKit 中有很多代码可以得到相同的结果。下面提供了代码和输出。
注意:阅读提供的评论
代码
class ImageCardWithButton: UIView {
lazy var cardImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false // To flag that we are using Constraints to set the layout
image.image = UIImage(named: "dog")
image.contentMode = .scaleAspectFill
return image
}()
lazy var gradientView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false // IMPORTANT IF YOU ARE USING CONSTRAINTS INSTEAD OF FRAMES
return view
}()
// VStack equivalent in UIKit
lazy var contentStack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.distribution = .fillProportionally // Setting the distribution to fill based on the content
return stack
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.numberOfLines = 0 // Setting line number to 0 to allow sentence breaks
label.text = "Let your curiosity do the booking"
label.font = UIFont(name: "Raleway-Semibold", size: 20) // Custom font defined for the project
label.textColor = .white
return label
}()
lazy var cardButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .white
button.setTitle("I'm flexible", for: .normal)
button.setTitleColor(.blue, for: .normal)
// button.addTarget(self, action: #selector(someObjcMethod), for: .touchUpInside) <- Adding a touch event and function to invoke
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
self.addSubview(cardImage) // Adding the subview to the current view. i.e., self
// Setting the corner radius of the view
self.layer.cornerRadius = 10
self.layer.masksToBounds = true
NSLayoutConstraint.activate([
cardImage.leadingAnchor.constraint(equalTo: self.leadingAnchor),
cardImage.trailingAnchor.constraint(equalTo: self.trailingAnchor),
cardImage.topAnchor.constraint(equalTo: self.topAnchor),
cardImage.bottomAnchor.constraint(equalTo: self.bottomAnchor),
])
setupGradientView()
addTextAndButton()
}
private func setupGradientView() {
let height = self.frame.height * 0.9 // Height of the translucent gradient view
self.addSubview(gradientView)
NSLayoutConstraint.activate([
gradientView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
gradientView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
gradientView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
gradientView.heightAnchor.constraint(equalToConstant: height)
])
// Adding the gradient
let colorTop = UIColor.clear
let colorBottom = UIColor.black
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [colorTop.cgColor, colorBottom.cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.frame = CGRect(
x: 0,
y: self.frame.height - height,
width: self.frame.width,
height: height)
gradientView.layer.insertSublayer(gradientLayer, at:0)
print(self.frame)
}
private func addTextAndButton() {
// Adding the views to the stackview
contentStack.addArrangedSubview(titleLabel)
contentStack.addArrangedSubview(cardButton)
gradientView.addSubview(contentStack)
NSLayoutConstraint.activate([
contentStack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20),
contentStack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20), // Negative for leading and bottom constraints
contentStack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -20), // Negative for leading and bottom constraints
cardButton.heightAnchor.constraint(equalToConstant: 60)
])
cardButton.layer.cornerRadius = 30 // Half of the height of the button
}
}
输出
重要提示
您可以使用约束或框架创建布局。如果您使用约束,重要 将视图 .translatesAutoresizingMaskIntoConstraints
设置为 false(您可以阅读它的文档)。
NSLayoutConstraint.activate([...])
用于一次应用一组约束。或者,您可以使用:
cardImage.leadingAnchor.constraint(...)isActivated = true
对于个别约束
- 视图的手动布局有时需要填充。因此,为此您必须根据您所在视图的边缘(侧面)为填充使用负值或正值。很容易记住将填充值设置为视图中心的方向。
E.x., 从 leading/left 边开始,您需要向视图中心添加 10 的填充或从 right/trailing 侧向中心添加 -10 .
我正在尝试创建类似 this 的内容。我最近一直在使用 SwiftUI,所以我知道我可以通过向 zstack 添加图像、文本和按钮(我很灵活的文本是 button/NavigationLink 的标签)来创建它。但我环顾四周,想看看 UIKit 是否有办法做到这一点。最好不使用故事板。我对 cocoapods 库或任何需要的东西持开放态度。我环顾四周,探索使用 SwiftUI 创建所需的 ZStack,然后在我的 UIKit 中使用它和 UIHostingController,但因为它涉及 button/navigationlink。看到 NavigationLink 如何要求目的地符合视图,我想在将我的更多项目转换为 swiftui 之前四处询问。我更希望这个项目能给我更多在没有故事板的情况下在 UIKit 中构建视图的经验,所以我更愿意这样做而不是使用 SwiftUI。如果可能的话我想。
我尝试四处搜索,但我所有 google 涉及 UIButton 和图像的搜索都只是 link 到关于在 UIButton 中设置图像的帖子。
由于您想获得更多使用 UIKit
创建视图的经验,我创建了一个继承自 UIView
的视图,您可以重复使用。在 UIKit 中有很多代码可以得到相同的结果。下面提供了代码和输出。
注意:阅读提供的评论
代码
class ImageCardWithButton: UIView {
lazy var cardImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false // To flag that we are using Constraints to set the layout
image.image = UIImage(named: "dog")
image.contentMode = .scaleAspectFill
return image
}()
lazy var gradientView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false // IMPORTANT IF YOU ARE USING CONSTRAINTS INSTEAD OF FRAMES
return view
}()
// VStack equivalent in UIKit
lazy var contentStack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.distribution = .fillProportionally // Setting the distribution to fill based on the content
return stack
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.numberOfLines = 0 // Setting line number to 0 to allow sentence breaks
label.text = "Let your curiosity do the booking"
label.font = UIFont(name: "Raleway-Semibold", size: 20) // Custom font defined for the project
label.textColor = .white
return label
}()
lazy var cardButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .white
button.setTitle("I'm flexible", for: .normal)
button.setTitleColor(.blue, for: .normal)
// button.addTarget(self, action: #selector(someObjcMethod), for: .touchUpInside) <- Adding a touch event and function to invoke
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
self.addSubview(cardImage) // Adding the subview to the current view. i.e., self
// Setting the corner radius of the view
self.layer.cornerRadius = 10
self.layer.masksToBounds = true
NSLayoutConstraint.activate([
cardImage.leadingAnchor.constraint(equalTo: self.leadingAnchor),
cardImage.trailingAnchor.constraint(equalTo: self.trailingAnchor),
cardImage.topAnchor.constraint(equalTo: self.topAnchor),
cardImage.bottomAnchor.constraint(equalTo: self.bottomAnchor),
])
setupGradientView()
addTextAndButton()
}
private func setupGradientView() {
let height = self.frame.height * 0.9 // Height of the translucent gradient view
self.addSubview(gradientView)
NSLayoutConstraint.activate([
gradientView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
gradientView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
gradientView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
gradientView.heightAnchor.constraint(equalToConstant: height)
])
// Adding the gradient
let colorTop = UIColor.clear
let colorBottom = UIColor.black
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [colorTop.cgColor, colorBottom.cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.frame = CGRect(
x: 0,
y: self.frame.height - height,
width: self.frame.width,
height: height)
gradientView.layer.insertSublayer(gradientLayer, at:0)
print(self.frame)
}
private func addTextAndButton() {
// Adding the views to the stackview
contentStack.addArrangedSubview(titleLabel)
contentStack.addArrangedSubview(cardButton)
gradientView.addSubview(contentStack)
NSLayoutConstraint.activate([
contentStack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20),
contentStack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20), // Negative for leading and bottom constraints
contentStack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -20), // Negative for leading and bottom constraints
cardButton.heightAnchor.constraint(equalToConstant: 60)
])
cardButton.layer.cornerRadius = 30 // Half of the height of the button
}
}
输出
重要提示
您可以使用约束或框架创建布局。如果您使用约束,重要 将视图
.translatesAutoresizingMaskIntoConstraints
设置为 false(您可以阅读它的文档)。NSLayoutConstraint.activate([...])
用于一次应用一组约束。或者,您可以使用:
cardImage.leadingAnchor.constraint(...)isActivated = true
对于个别约束
- 视图的手动布局有时需要填充。因此,为此您必须根据您所在视图的边缘(侧面)为填充使用负值或正值。很容易记住将填充值设置为视图中心的方向。
E.x., 从 leading/left 边开始,您需要向视图中心添加 10 的填充或从 right/trailing 侧向中心添加 -10 .