ViewController:
class ViewController: UIViewController, ViewSpecificController {
typealias RootView = CustomView
override func viewDidLoad() {
super.viewDidLoad()
view().configure()
}
override func loadView() { self.view = CustomView() }
}
UIView:
class CustomView: UIView {
func configure() {
backgroundColor = .orange
translatesAutoresizingMaskIntoConstraints = false
addConstraints()
}
func addConstraints() {
var constraints = [NSLayoutConstraint]()
constraints.append(self.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor))
constraints.append(self.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor))
constraints.append(self.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor))
constraints.append(self.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor))
NSLayoutConstraint.activate(constraints)
}
}
Executing this code results in an error "[LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want."
I tried to initialize UIView, the same error appeared there. How to fix it?
From what i can see it looks like your CustomView class is trying to set constraints to itself. The constraints aren't needed as the ViewController will handle sizing it automatically once you replace the original in loadView(). Removing your addConstraints() method from configure() should solve your problem. See if that works...
Related
When I try to compile this code I get compile time error that's saying:
Protocol 'KeyboardScrollManagable' can only be used as a generic constraint because it has Self or associated type requirements
If I remove lines with //One and //Two comments it will work, but then AbcView doesn't have to be Scrollable anymore. I want to enforce that if you use KeyboardScrollManagable on any a ViewController's subclass, it's View will need to be Scrollable!
protocol KeyboardScrollManagable: UIViewController {
associatedtype View where View: Scrollable //One
var customView: View { get } //Two
func doSomething()
}
class ViewController<ViewModel, View: UIView>: UIViewController {
let viewModel: ViewModel
let customView: View
init(view: View, viewModel: ViewModel) {
self.viewModel = viewModel
self.customView = view
super.init(nibName: nil, bundle: nil)
}
override func loadView() {
view = customView
}
override func viewDidLoad() {
super.viewDidLoad()
(self as? KeyboardScrollManagable)?.doSomething() //ERROR:
//Protocol 'KeyboardScrollManagable' can only be used as a generic constraint because it has Self or associated type requirements
}
}
protocol Scrollable: UIView {
var scrollView: UIScrollView { get }
}
final class AbcView: UIView, Scrollable {
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .clear
return scrollView
}()
//rest of the impl
}
final class AbcViewController: ViewController<AbcViewModel, AbcView> {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension AbcViewController: KeyboardScrollManagable {}
I'm stuck! Any ideas? Or am I asking too much from the Swift?
I have a view controller which has a programmatically created label like below.
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupUI()
}
func setupUI() {
// added an setup the view constraints.
}
}
This works properly. Then I tried to move all the UI element of the view controller to it's extension by creating a extension like below :
private extension MyController {
var label: UILabel = {
**// tried to initialize the label like this. but getting cannot use stored property in extension error**
}()
// then tried like below
var mylabel: UILabel! {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello"
return label
}
func setupUI() {
// with the second option I getting nil value error.
}
}
How can I initialize UI elements in viewcontroller extentions programmatically to access internally.
Try this:
uielements.swift
extension MyController {
static let myLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Hello"
return label
}()
}
myController.swift
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.addSubview(RootVC.myLabel)
// Add layout constraints here
}
}
As the error says you are not allowed to declare variables in extension.
You mention that you want to initialize the UI element in the
extensions. of course you can do that. But, what you have in the
extension is a declaration not only initialization.
Have the declaration in the controller and the initialization in an extension method.
I want to put BlueView under NavigationBar on TableViewController. But it does not work.
like [1]: https://imgur.com/a/5WcQQ
class TableViewController: UITableViewController {
private func setupView() {
view.addSubview(blueView)
blueView.translatesAutoresizingMaskIntoConstraints = false
blueView.snp.makeConstraints { (make) in
make.left.equalTo(view.snp.left)
make.right.equalTo(view.snp.right)
make.top.equalTo(view.safeAreaLayoutGuide.snp.topMargin)
make.height.equalTo(50)
}
}
}
I found that it works by using viewController with tableview.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
private func setupView() {
view.addSubview(blueView)
blueView.translatesAutoresizingMaskIntoConstraints = false
blueView.snp.makeConstraints { (make) in
make.left.equalTo(view.snp.left)
make.right.equalTo(view.snp.right)
make.top.equalTo(view.safeAreaLayoutGuide.snp.topMargin)
make.height.equalTo(50)
}
}
}
But I really want to know why? Or anything else ideas about safeAreaLayoutGuide on UITableViewController.
I think the tableViewController consists of a tableView covering everything, hence you wont be able to place a view in between the tableView and the navigationbar. You can try bringing your blueView on top of the tableView by using view.bringSubview(toFront: blueView)
Secondly your code is a bit off from the best practice.
blueView.snp.makeConstraints { (make) in
make.leading.trailing.top.equalToSuperview()
make.height.equalTo(50)
}
This should have the exact same effect as your code but much shorter.
Edit: If this causes the view to be located below your navigationBar set edgesForExtendedLayout = [] in your viewDidLoad() or setup function
I created a nib file with a custom collectionViewCell and attached to a viewController
class CustomCollectionView: UICollectionViewCell{}
Now I have to use the exact cell inside a tableView. I created a new nib file and viewController
class CustomTableView: UITableViewCell{}
and I copied the hole code of CustomCollectionView on it. every thing is working fine but I believe that it dose not make sense to copy the hole exact code of CustomCollectionView into CustomTableView and to use the exact same nib file but with a tableViewCell instead of collectionViewCell on it. Is there any way to optimize what I did?
As you said in a comment in suhit's answer, you can do this by using a common view in both the CollectionViewCell and TableViewCell subclasses. You don't need a ViewController since it adds extra overhead. A simple UIView is enough. Some code to show what I mean:
class CustomTableViewCell: UITableViewCell {
var customView: CustomView!
func awakeFromNib() {
super.awakeFromNib()
customView = CustomView()
customView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(customView)
customView.fillSuperview()
}
}
class CustomCollectionViewCell: UICollectionViewCell {
var customView: CustomView!
func awakeFromNib() {
super.awakeFromNib()
customView = CustomView()
customView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(customView)
customView.fillSuperview()
}
}
extension UIView {
func fillSuperview() {
guard let superview = superview else {
return print("no superview")
}
topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true
rightAnchor.constraint(equalTo: contentVisuperviewew.rightAnchor).isActive = true
}
}
A sample implementation for the CustomView class:
class CustomView: UIView {
func initialize() {
//...
}
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
}
If you wish to create your custom view in a xib that's also fine, but it's a little trickier. This is beyond the scope of the question but I'm just going to leave a link here in case you need it.
If you want to use same view then its better to use similar type view i.e. use collectionView at both places so that you can use the CustomCollectionViewCell in both ViewControllers. UICollectionView is highly customisable so you can do whatever you want to do with UITableView in UICollectionView as well.
I have a #IBDesignable UIView (ContainerView) that has one subView (also #IBDesignable). I would very much like to be able to update the constraints of the subView in the code in a way were they are automatically updated in InterfaceBuilder. Example:
#IBDesignable class ContainerView: UIView {
#IBOutlet var mySubView: MyView!
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.setup()
}
private func setup() {
self.mySubView.leadingConstraint.constant = MyResource.sizes.defaultLeading
}
}
This will work just fine at runtime, but it crashes the IBDesignablesAgent because mySubView is nil when running prepareForInterfaceBuilder.
I want to do it this way to be able to set my constraints globally in some constants, but keep the view representation in my xib files.
Does anyone have a work around for this, or am I reaching for the impossible here?