UIVIewController Not Getting Deinitialized When Popping - ios

I am building a settings screen in an app where I have a list of cells. If a user taps on a cell it pushes another controller onto the stack. However, I have this flow in several places in my app.
Therefore, I decided to reuse a generic controller and initialize it with sections (depending on which cell was tapped)
However, when popping a UIViewController it isn't getting deinitialized
VIEW CONTROLLER CODE
// Class
class ProfileController: UIViewController {
private let authService: AuthSerivce
private let sections: [FormSectionComponent]
init(authService: AuthSerivce,
sections: [FormSectionComponent]) {
self.authService = authService
self.sections = sections
super.init(nibName: nil, bundle: nil)
}
}
// Cell Delegate
extension ProfileController: NavigateCellDelegate {
func navigate(cell: NavigateCell) {
guard let sections = cell.item?.components else { return }
let controller = ProfileController(authService: authService, sections: sections)
self.navigationController?.pushViewController(controller, animated: true)
}
}
CELL CODE
protocol NavigateCellDelegate {
func navigate(cell: NavigateCell)
}
class NavigateCell: UICollectionViewCell {
var item: NavigateComponent?
var delegate: NavigateCellDelegate?
lazy var titleLabel: UILabel = {
let view = UILabel()
view.numberOfLines = 0
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(frame: CGRect) {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func bind(_ item: FormItemComponent) {
guard let item = item as? NavigateComponent else { return }
self.item = item
setUpView(item: item)
addTapGestureRecogniser()
}
func addTapGestureRecogniser() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapGesture))
self.addGestureRecognizer(tapGesture)
self.isUserInteractionEnabled = true
}
#objc func tapGesture() {
delegate?.navigate(cell: self)
}
override func prepareForReuse() {
super.prepareForReuse()
titleLabel.text = ""
}
}
extension NavigateCell {
func setUpView(item: NavigateComponent) {
titleLabel.text = item.title
addSubview(titleLabel)
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: topAnchor),
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
} // END
UPDATED WEAK DELEGATE IN CELL
protocol NavigateCellDelegate: AnyObject {
func navigate(cell: NavigateCell)
}
class NavigateCell: UICollectionViewCell {
weak var item: NavigateComponent?
weak var delegate: NavigateCellDelegate?

Figured it out - The problem was with my DiffableDataSource and not declaring [weak self]
return UICollectionViewDiffableDataSource(collectionView: profileView.collectionView) { [weak self] collectionView, indexPath, item in

Related

How to implement a text field cell with custom content configuration in iOS 14?

Apple's Implementing Modern Collection Views has an example of implementing custom UICollectionViewCell in Modern Collection Views/Cell Configurations. It demonstrates how to add custom views to UICollectionViewCell.
What I want is it's a UITextField cell. So I added a UITextField and the code was:
class CustomConfigurationCell: UICollectionViewListCell {
// Trying to gain access to UITextField
// But contentView is not a CustomContentView
var textField: UITextField? {
guard let contentView = contentView as? CustomContentView else {
return nil
}
return contentView.textField
}
override func updateConfiguration(using state: UICellConfigurationState) {
var content = CustomContentConfiguration().updated(for: state)
content.name = name
contentConfiguration = content
}
}
struct CustomContentConfiguration: UIContentConfiguration, Hashable {
func makeContentView() -> UIView & UIContentView {
return CustomContentView(configuration: self)
}
}
class CustomContentView: UIView, UIContentView {
public let textField = UITextField();
}
In my UIViewController:
let textFieldCellRegistration = UICollectionView.CellRegistration<CustomConfigurationCell, Key> { (cell, indexPath, item) in
cell.textField?.delegate = self
}
The text field showed up as expected. But unfortunately, cell.textField was nil. I couldn't retrieve user input without UITextViewDelegate.
So how can I implement a text field cell with content configuration?
You just need a custom content configuration and view. Note: you'll might have to implement your own indentation and margins to match UIListContentConfiguration.
import UIKit
struct TextFieldContentConfiguration: UIContentConfiguration {
var text: String? = nil
var textChanged: ((String?) -> Void)?
func makeContentView() -> UIView & UIContentView {
return TextFieldContentView(configuration: self)
}
func updated(for state: UIConfigurationState) -> TextFieldContentConfiguration {
return self
}
}
class TextFieldContentView: UIView, UIContentView, UITextFieldDelegate {
public var configuration: UIContentConfiguration {
get {
return appliedConfiguration
}
set {
if let config = newValue as? TextFieldContentConfiguration {
apply(configuration: config)
}
}
}
private var appliedConfiguration: TextFieldContentConfiguration = TextFieldContentConfiguration()
private func apply(configuration: TextFieldContentConfiguration) {
textField.text = configuration.text
self.appliedConfiguration = configuration
}
required init(configuration: TextFieldContentConfiguration) {
textField = UITextField(frame: .zero)
textField.translatesAutoresizingMaskIntoConstraints = false
super.init(frame: .zero)
addViews()
apply(configuration: configuration)
textFieldToken = NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: textField, queue: .main, using: { [weak self] notification in
guard let textField = notification.object as? UITextField else {
return
}
self?.appliedConfiguration.textChanged?(textField.text)
})
}
#available(*, unavailable)
required init?(coder: NSCoder) {
fatalError()
}
private func addViews() {
addSubview(textField)
let guide = layoutMarginsGuide
NSLayoutConstraint.activate([
textField.topAnchor.constraint(equalTo: guide.topAnchor),
textField.bottomAnchor.constraint(equalTo: guide.bottomAnchor),
textField.leadingAnchor.constraint(equalTo: guide.leadingAnchor),
textField.trailingAnchor.constraint(equalTo: guide.trailingAnchor),
])
}
private let textField: UITextField
private var textFieldToken: Any?
}
Then you use the custom configuration with a UICollectionViewListCell, and pass a block that will get the value back.
let textCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String?> {
[weak self] cell, indexPath, model in
var config = TextFieldContentConfiguration()
config.text = self?.timer.displayName
config.textChanged = { newText in
self?.timer.displayName = newText ?? ""
}
cell.contentConfiguration = config
}

didSet property value only appears in print()

INITIAL GOAL:
Have a view with a list of cells positioned vertically displaying some information. As soon as the user clicks on a cell to show a new view with more information.
THE ROAD SO FAR (curry on my wayward son!):
I created 2 view controllers: ViewController (subclassing UICollectionViewController, UICollectionViewDelegateFlowLayout) and DetailViewController (subclassing UIViewController).
I created a Cell that the ViewController uses to generate the collection view and a DetailView that the DetailViewController uses
I created a struct named Detail as a custom data type which provides storage of data using properties (ex. name, surname, address, etc.)
The struct:
struct Detail: Decodable {
let name: String?
let surname: String?
let address: String?
let description: String?
}
I use the following data for testing (after the testing is done I will get this data from an API call). I placed it inside ViewController:
let details: [Detail] = [Detail(name: "Chris", surname: "Doe", address: "Neverland 31", description: "This is a description about Chris Doe"), Detail(name: "Tony", surname: "Cross", address: "Galaxy Road 1", description: "This is a description about Tony Cross")]
To create the cells using the information above and the method:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
And also:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! Cell
As the method requires us to return a UICollectionViewCell, I first send the associated information to Cell by doing the following:
cell.details = details[indexPath.item]
return cell
Inside the Cell I created the following property using didSet to help me retrieve the information:
var details: Detail? {
didSet {
guard let details = details else { return }
guard let name = details.name else { return }
....
....
}
As you can understand using the information coming from ViewController I dynamically constructed each cell.
All were good at this point.
Then I tried to show a detailed view when clicking on a cell. To do this I followed the same practice inside the method:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let detailView = DetailView()
detailView.details = details[indexPath.item]
let detailViewController = DetailViewController()
detailViewController.modalTransitionStyle = .coverVertical
self.present(detailViewController, animated: true, completion: nil)
}
Again, in the DetailView I use the same approach to get the data associated with the selected item. This way I can have access to the data of the cell the user selects, as shown below:
import UIKit
class DetailView: UIView {
var dismissDetailViewAction: (() -> Void)?
var details: Detail? {
didSet {
// get details
guard let details = details else { return }
guard let name = details.name else { return }
guard let surname = details.surname else { return }
guard let address = details.address else { return }
guard let description = details.description else { return }
// print description and it shows in the console but not in the view
print(description)
let attributedTextDescription = NSMutableAttributedString(string: description, attributes: [NSAttributedStringKey.font: UIFont.FontBook.AvertaRegular.of(size: 20), NSAttributedStringKey.foregroundColor: UIColor.white])
briefDescription.attributedText = attributedTextDescription
briefDescription.textAlignment = .center
briefDescription.textColor = .white
briefDescription.numberOfLines = 0
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not yet been implemented")
}
fileprivate func setupView() {
setupDescriptionText()
setupCloseButton()
}
let briefDescription: UITextView = {
let text = UITextView()
text.textColor = .red
return text
}()
let closeButton: UIButton = {
let button = UIButton(title: "Close", font: UIFont.FontBook.AvertaRegular.of(size: 18), textColor: .white, cornerRadius: 5)
button.backgroundColor = .black
button.addTarget(self, action: #selector(closeDetailView), for: .touchUpInside)
return button
}()
fileprivate func setupDescriptionText() {
self.addSubview(briefDescription)
briefDescription.translatesAutoresizingMaskIntoConstraints = false
briefDescription.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5).isActive = true
briefDescription.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -5).isActive = true
briefDescription.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
briefDescription.heightAnchor.constraint(equalToConstant: 300).isActive = true
}
fileprivate func setupCloseButton() {
self.addSubview(closeButton)
closeButton.translatesAutoresizingMaskIntoConstraints = false
closeButton.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
closeButton.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
closeButton.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 40).isActive = true
closeButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -40).isActive = true
closeButton.heightAnchor.constraint(equalToConstant: 60).isActive = true
}
#objc func closeDetailView() {
dismissDetailViewAction?()
}
}
So, what I actually do is to design the static part of the view outside didSet, and what is dynamic part inside didSet. This works with the cells of collectionView.
I use the DetailViewController to display the DetailView and dismiss itself when the user clicks on the "Close" button.
import UIKit
class DetailViewController: UIViewController {
// reference DetailView view
var detailView: DetailView!
override func viewDidLoad() {
super.viewDidLoad()
// setup view elements
setupView()
}
fileprivate func setupView() {
let mainView = DetailView(frame: self.view.frame)
self.detailView = mainView
self.view.addSubview(detailView)
self.homeDetailView.dismissDetailViewAction = dismissDetailView
// pin view
self.detailView.translatesAutoresizingMaskIntoConstraints = false
self.detailView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.detailView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.detailView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.detailView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
}
fileprivate func dismissDetailView() {
// dismiss current (DetailViewController) controller
self.dismiss(animated: true, completion: nil)
}
}
The reason I did this is that I like to keep my ViewControllers as clean as possible (Massive View Controller, not my thing).
THE PROBLEM
The whole thing is built without any problem, but when I click on a cell to go to the DetailView no information is displayed.
THE WEIRD PART
Inside the DetailView --> didSet, when I use print(name), it works just fine (you see the correct name inside console). But when I try to use that value inside the view it will not be displayed.
And I know that my DetailView is just fine since if I use hardcoded values in it, it works (you see the correct result).
Any advise why this is not working properly?
PS: I am building the whole thing programmatically. No storyboards involved.
Thanks in advance and sorry for the lost post.
As was mentioned, your detailView is not referenced inside detailViewController. Instead, you create another instance of DetailView inside DetailViewController but this one has no Detail in it.
The console message was called from inside your detailView, but inside detailViewController is another instance that did not call this message, because its Detail is set to nil by default.
To be short, to fix that you should simply do the following changes:
import UIKit
class DetailViewController: UIViewController {
var detail: Detail!
private lazy var detailView: DetailView = {
let mainView = DetailView(frame: self.view.frame)
mainView.details = detail
return mainView
}
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
fileprivate func setupView() {
self.view.addSubview(detailView)
self.homeDetailView.dismissDetailViewAction = dismissDetailView
// pin view
self.detailView.translatesAutoresizingMaskIntoConstraints = false
self.detailView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.detailView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.detailView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.detailView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
}
fileprivate func dismissDetailView() {
// dismiss current (DetailViewController) controller
self.dismiss(animated: true, completion: nil)
}
}
And inside your collectionView(...) func:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let detailViewController = DetailViewController()
detailViewController.detail = details[indexPath.item]
detailViewController.modalTransitionStyle = .coverVertical
self.present(detailViewController, animated: true, completion: nil)
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let detailView = DetailView()
detailView.details = details[indexPath.item]
let detailViewController = DetailViewController()
detailViewController.modalTransitionStyle = .coverVertical
self.present(detailViewController, animated: true, completion: nil)
}
You make a DetailView here, pass it your details... and then do nothing with it.
Normally DetailView would be a property of the DetailViewController and you'd pass the details to the view controller, which would display it.
What's happening here is that you're creating, configuring and throwing away a DetailView, when you probably should be using the one that DetailViewController owns, or should own.

how canBecomeFirstResponder bring custom inputAccessoryView

I'm trying to make custom inputAccessoryView contain UITextField and docking it bottom like chat application
but if i didn't use canBecomeFirstResponder = true inputAccessoryView are hidden(?)
this is my code
class MainVC: UITableViewController {
lazy var inputTextFieldContainer: UIView = {
// custom inputAccessoryView
}()
override func viewDidLoad(){
super.viewDidLoad()
}
override var inputAccessoryView: UIView? {
get{
return containerView
}
}
override var canBecomeFirstResponder: Bool {
get {
return true
}
}
}
how canBecomeFirstResponder bring custom inputAccessoryView
i read about UIResponder and responder chain on Apple docs
but i couldn't match that concept to this issue.
They said UIResponder will handle the events and i make my MainVC become first responder by canBecomeFirstResponder = true
and inputAccessoryView are shown
but what is the exact event in this case and situation
Since your code inherits from UITableViewController here a complete example with it:
Start with defining a accessory view. Since you mentioned as an example a chat app it could be a textfield and a send button:
class SendMessageView: UIView {
private let textField = UITextField()
private let sendButton = UIButton()
init() {
super.init(frame: CGRect.zero)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) not implemented")
}
private func setup() {
sendButton.setTitle("Send", for: UIControlState.normal)
sendButton.setTitleColor(UIColor.blue, for: UIControlState.normal)
self.addSubview(textField)
self.addSubview(sendButton)
self.backgroundColor = UIColor.groupTableViewBackground
textField.backgroundColor = UIColor.white
self.autoresizingMask = .flexibleHeight
textField.translatesAutoresizingMaskIntoConstraints = false
sendButton.translatesAutoresizingMaskIntoConstraints = false
textField.topAnchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true
textField.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
sendButton.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 8).isActive = true
sendButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
sendButton.centerYAnchor.constraint(equalTo: textField.centerYAnchor).isActive = true
}
override var intrinsicContentSize: CGSize {
let contentHeight = self.textField.intrinsicContentSize.height + 16
return CGSize(width: UIScreen.main.bounds.width, height: contentHeight)
}
}
Next you need a custom UITableView which uses our accessory view:
class CustomTableView: UITableView {
private let sendMessageView = SendMessageView()
override var canBecomeFirstResponder: Bool {
return true
}
override var inputAccessoryView: UIView? {
return self.sendMessageView
}
}
Finally one could define a TableViewController using this custom table view:
class TableViewController: UITableViewController {
override func loadView() {
self.tableView = CustomTableView()
self.view = self.tableView
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.becomeFirstResponder()
}
...
The result would look like this:
[

Constraints not working when device is rotated

I have this UIViewController:
import UIKit
class ViewController: UIViewController {
var object: DraggableView?
override func viewDidLoad() {
super.viewDidLoad()
// Create the object
object = DraggableView(parent: self)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Add subview
object?.setup()
}
}
And I have this class to add the view in this VC:
import UIKit
class DraggableView {
var parent: UIViewController!
let pieceOfViewToShow: CGFloat = 30.0
init(parent: UIViewController) {
self.parent = parent
}
func setup() {
let view = UIView(frame: parent.view.frame)
view.backgroundColor = UIColor.red
parent.view.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.leadingAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.trailingAnchor).isActive = true
view.heightAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.heightAnchor).isActive = true
// I need to show only a piece of the view at bottom, so:
view.topAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.topAnchor, constant: parent.view.frame.height - pieceOfViewToShow).isActive = true
}
}
Problem
Everything is correct but when the device rotates it loses the constraint and the added view is lost.
I think the problem is in the next line that is not able to update the correct height [parent.view.frame.height] when the device is rotated.
view.topAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.topAnchor, constant: parent.view.frame.height - pieceOfViewToShow).isActive = true
How could I make to update this constant when rotating?
I'm using Swift 3.
You can try using traitCollectionDidChange callback on the UIView to update the constraint when a rotation changes, for that to work you'll need to make DraggableView a subclass of the UIView:
import UIKit
class DraggableView: UIView {
var parent: UIViewController!
let pieceOfViewToShow: CGFloat = 30.0
// keep the constraint around to have access to it
var topConstraint: NSLayoutConstraint?
init(parent: UIViewController) {
super.init(frame: parent.view.frame)
self.parent = parent
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.backgroundColor = UIColor.red
parent.view.addSubview(self)
self.translatesAutoresizingMaskIntoConstraints = false
self.leadingAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.leadingAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.trailingAnchor).isActive = true
self.heightAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.heightAnchor).isActive = true
// keep a reference to the constraint
topConstraint = self.topAnchor.constraint(equalTo: parent.view.safeAreaLayoutGuide.topAnchor, constant: parent.view.frame.height - pieceOfViewToShow)
topConstraint?.isActive = true
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
// update the constraints constant
topConstraint?.constant = parent.view.frame.height - pieceOfViewToShow
}
}

Delegate nil after setting it

I am using the delegate method but for some odd reason my delegate variable seems to be nil when I want to call the delegate method. I can't for the life of me figure out what I'm doing wrong
protocol ProfileProtocol {
func buttonTapped()
}
class ProfileView: UIView {
var delegate: ProfileProtocol?
#IBOutlet weak var button: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func awakeFromNib() {
configure()
}
func setup() {
...
}
#IBAction func buttonTapped(_ sender: UIButton) {
// delegate nil
delegate?.buttonTapped()
}
}
ProfileViewController (yes it conforms to ProfileProtocol):
override func viewDidLoad() {
swipeableView.nextView = {
createCardView()
}
}
func createCardView() -> UIView {
let cardView = ProfileView(frame: swipeableView.bounds)
cardView.delegate = self
let contentView = Bundle.main.loadNibNamed("ProfileCardView", owner: self, options: nil)?.first! as! UIView
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.backgroundColor = cardView.backgroundColor
cardView.addSubview(contentView)
activeCardView = cardView
return cardView
}
func buttonTapped() {
self.performSegue(withIdentifier: "profileToEmojiCollection", sender: self)
}
Whenever I tap the button in my ProfileView, my ProfileViewController should perform a segue, however the delegate method isn't even being called because delegate is nil when I tap the button
I like to keep my custom views modular, and do things programmatically, it avoids the use of a Xib.
You should keep your view's responsibilities and subviews to the view itself. Ultimately the View receiving the the action(s) should be responsible for calling the delegate's methods. Also nextView is a closure that returns a UIView: (() -> UIView?)? not a UIView, a call to a function in a closure is not an explicit return you should return the view: let view = createCardView() return view.
ProfileView.swift
import UIKit
protocol ProfileProtocol {
func buttonTapped()
}
class ProfileView: UIView {
var delegate: ProfileProtocol?
lazy var button: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
button.setTitle("Profile Button", for: .normal)
button.backgroundColor = UIColor.black
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
}
#objc func buttonTapped(_ sender: UIButton) {
// Check for a nil delegate, we dont want to crash if one is not set
if delegate != nil {
delegate!.buttonTapped()
} else {
print("Please set ProfileView's Delegate")
}
}
func setup() {
//setup subviews
self.addSubview(button)
button.widthAnchor.constraint(equalToConstant: 150).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
}
You can create ProfileView's like any other UIView, but remember to set the Delegate of each of them after creation:
swipeableView.nextView = {
let view = createProfileView() //set properties during creation?
view.delegate = self
//set properties after creation?
//view.backgroundColor = UIColor.red
return view
}
ViewController.swift
import UIKit
class ViewController: UIViewController, ProfileProtocol {
lazy var profileView: ProfileView = {
let view = ProfileView()
view.backgroundColor = UIColor.lightGray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
profileView.delegate = self
setup()
}
func buttonTapped() {
print("Do Something")
}
func setup() {
self.view.addSubview(profileView)
profileView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
profileView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.7).isActive = true
profileView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
profileView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Resources