UINavigationController
will easily solve this problem using the navigation bar.UINavigationController
is just a stack to which you move a multitude of views. The most top view (the one that was moved last) is visible to the user right now (except when you have another view, presented on top of this, let's say favorites). And when you press the top view controllers of the navigation controller, the navigation controller automatically creates a “back” button (upper left or right side depending on the current language preferences of the device), and pressing this button returns you to the previous view.viewController = ViewController ():
let navigationController = UINavigationController(rootViewController: viewController)
self.window? .RootViewController = viewController self.window? .RootViewController = navigationController
self.window? .RootViewController = viewController self.window? .RootViewController = navigationController
self.window? .RootViewController = viewController self.window? .RootViewController = navigationController
. In the first line, we created an instance of UINavigationController
and passed it our viewController
as rootViewController
, which is the view controller at the very bottom of the stack, which means that there will never be a back button on the navigation bar of this view. Then we give our window a navigation controller as rootViewController
, since now it will contain all the views in the application.upperView
to match the height of the navigation bar.isTranslucent
property to false
. This will make the navigation bar opaque (if you didn’t notice, it is slightly transparent), and now the top edge of the superview will become the bottom of the navigation bar.UINavigationController
and UINavigationBar
:self.navigationController? .NavigationBar.isTranslucent = false super.viewDidLoad ()
self.navigationController? .NavigationBar.isTranslucent = false super.viewDidLoad ()
, so that it would look like this: override func viewDidLoad() { super.viewDidLoad() self.navigationController?.navigationBar.isTranslucent = false self.view.backgroundColor = .white self.addSubviews() self.setupConstraints() self.view.bringSubview(toFront: avatar) self.view.setNeedsUpdateConstraints() }
self.title = "John Doe" viewDidLoad
, which will add a "Profile" to the navigation bar so that the user knows where he is at the moment. Start the application and the result should look like this:ViewController.swift
file in order to be able to use only real logic, and not just code for user interface elements. We can do this by creating a subclass of UIView
and moving all our user interface elements there. The reason we do this is to follow the Model-View-Controller or MVC architectural pattern for short. Learn more about MVC Model-View-Controller (MVC) in iOS: a modern approach .ContactCard
folder in the Project Navigator and select "New File":UIView
, and it will add some template code. Now click Next, then Create and remove the commented code: /* // Only override draw() if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func draw(_ rect: CGRect) { // Drawing code } */
init(frame :)
by typing init
, and then selecting the first autocomplete result from Xcode.super.init (frame: frame)
.addSubviews()
method under the initializers and remove self.view
before each addSubview
call. func addSubviews() { addSubview(avatar) addSubview(upperView) addSubview(segmentedControl) addSubview(editButton) }
override init(frame: CGRect) { super.init(frame: frame) addSubviews() bringSubview(toFront: avatar) }
updateConstraints()
and add a call at the end of this function (where it will always remain): override func updateConstraints() { // Insert code here super.updateConstraints() // Always at the bottom of the function }
override func updateConstraints() { avatar.autoAlignAxis(toSuperviewAxis: .vertical) avatar.autoPinEdge(toSuperviewEdge: .top, withInset: 64.0) upperView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .bottom) segmentedControl.autoPinEdge(toSuperviewEdge: .left, withInset: 8.0) segmentedControl.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0) segmentedControl.autoPinEdge(.top, to: .bottom, of: avatar, withOffset: 16.0) editButton.autoPinEdge(.top, to: .bottom, of: upperView, withOffset: 16.0) editButton.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0) super.updateConstraints() }
ProfileView
instance above the viewDidLoad
method let profileView = ProfileView(frame: .zero)
, add it as a subview to the ViewController
. import UIKit import PureLayout class ViewController: UIViewController { let profileView = ProfileView(frame: .zero) override func viewDidLoad() { super.viewDidLoad() self.navigationController?.navigationBar.isTranslucent = false self.title = "Profile" self.view.backgroundColor = .white self.view.addSubview(self.profileView) self.profileView.autoPinEdgesToSuperviewEdges() self.view.layoutIfNeeded() } }
ViewController.swift
and add lazy var
for tableView
above viewDidLoad()
: lazy var tableView: UITableView = { let tableView = UITableView() tableView.translatesAutoresizingMaskIntoConstraints = false tableView.delegate = self tableView.dataSource = self return tableView }()
UITableViewController
, and therefore we will add these two protocols to the class: class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { . . .
UITableViewDataSource
protocol, which means that there are mandatory methods in this protocol that are not defined in the class. To find out which of these methods you must implement, while holding Cmd + Control, click the UITableViewDataSource
protocol in the class definition and you will proceed to the protocol definition. For any method that is not preceded by the word optional
, a class corresponding to this protocol must be implemented.public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
- this method tells the table view how many rows we want to show.public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
- this method requests a cell in each row. Here we initialize (or reuse) the cell and insert the information we want to show to the user. For example, the first cell will display the phone number, the second cell will display the address, and so on.ViewController.swift
, start typing numberOfRowsInSection
, and when auto-complete appears, select the first option. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { <#code#> }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 }
cellForRowAt
and select the first method from autocomplete again. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { <#code#> }
UITableViewCell
. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return UITableViewCell() }
ProfileView
, we define a new initializer that takes a table view as a parameter so that it can add it as a subview and set the appropriate restrictions for it.ProfileView.swift
and add an attribute for the table view directly above the initializer:var tableView: UITableView!
determined, so we are not sure that he will be constantly.init (frame :)
implementation with: init(tableView: UITableView) { super.init(frame: .zero) self.tableView = tableView addSubviews() bringSubview(toFront: avatar) }
init (frame :)
for ProfileView
, so go back to ViewController.swift
and replace let profileView = ProfileView (frame: .zero)
with lazy var profileView: UIView = { return ProfileView(tableView: self.tableView) }()
ProfileView
has a link to the table view, and we can add it as a subview, and set the correct limits for it.ProfileView.swift
, add addSubview(tableView)
to the end of addSubviews()
and set these restrictions to updateConstraints()
above super.updateConstraints
: tableView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top) tableView.autoPinEdge(.top, to: .bottom, of: segmentedControl, withOffset: 8)
UITableViewCell
, we almost always need to create subclasses of this class, so right-click the ContactCard
folder in the Project Navigator, then “New file ...”, then “Cocoa Touch Class” and “Next”. lazy var titleLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "Title" return label }() lazy var descriptionLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.text = "Description" label.textColor = .gray return label }()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(titleLabel) contentView.addSubview(descriptionLabel) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func updateConstraints() { let titleInsets = UIEdgeInsetsMake(16, 16, 0, 8) titleLabel.autoPinEdgesToSuperviewEdges(with: titleInsets, excludingEdge: .bottom) let descInsets = UIEdgeInsetsMake(0, 16, 4, 8) descriptionLabel.autoPinEdgesToSuperviewEdges(with: descInsets, excludingEdge: .top) descriptionLabel.autoPinEdge(.top, to: .bottom, of: titleLabel, withOffset: 16) super.updateConstraints() }
UIEdgeInsets
to set intervals around each label. A UIEdgeInsets
object can be created using the UIEdgeInsetsMake(top:, left:, bottom:, right:)
method UIEdgeInsetsMake(top:, left:, bottom:, right:)
. For example, for titleLabel
we say that we want the upper limit to be four points, and the right and left eight. We do not care about the bottom because we exclude it, as we will attach it to the top of the description label. Take a minute to read and visualize all the constraints in your head.ViewController.swift
and change the lazy initialization of our table view to register this class of cells in the table view and set the height for each cell. let profileInfoCellReuseIdentifier = "profileInfoCellReuseIdentifier" lazy var tableView: UITableView = { ... tableView.register(ProfileInfoTableViewCell.self, forCellReuseIdentifier: profileInfoCellReuseIdentifier) tableView.rowHeight = 68 return tableView }()
UITableView
reuse cells that were previously presented to display new content instead of redrawing a new cell from scratch.cellForRowAt
method: func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: profileInfoCellReuseIdentifier, for: indexPath) as! ProfileInfoTableViewCell return cell }
ProfileInfoTableViewCell
so that we can access its properties so that we can, for example, set the title and description. This can be done using the following: func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { ... switch indexPath.row { case 0: cell.titleLabel.text = "Phone Number" cell.descriptionLabel.text = "+234567890" case 1: cell.titleLabel.text = "Email" cell.descriptionLabel.text = "john@doe.co" case 2: cell.titleLabel.text = "LinkedIn" cell.descriptionLabel.text = "www.linkedin.com/john-doe" default: break } return cell }
numberOfRowsInSection
to return "3" and start your application.ProfileInfoTableViewCell
add this line to the descriptionLabel
lazy initializer: label.numberOfLines = 0
ViewController
and add these two lines to the initializer of the table view: lazy var tableView: UITableView = { ... tableView.estimatedRowHeight = 64 tableView.rowHeight = UITableViewAutomaticDimension return tableView }()
“The table view can be improved.” - Apple Docs
ViewDidLoad
we need to reload the table view for these changes to take effect: override func viewDidLoad() { super.viewDidLoad() ... DispatchQueue.main.async { self.tableView.reloadData() } }
switch
to the cellForRow
: case 3: cell.titleLabel.text = "Address" cell.descriptionLabel.text = "45, Walt Disney St.\n37485, Mickey Mouse State"
main.storyboard
file from your project.UIWindow
programmatically and assigned it a rootViewController
.UINavigationBar
into your application.UITableViewCell
.Source: https://habr.com/ru/post/447374/
All Articles