trouble with constructs after adding view to contentView - ios

I need to add a target to an image in a tableview, but it doesn't work if I don't add a view to the contentView. After that, my constraints broke.
https://github.com/termyter/stackoverflow-search
beforeenter image description here
afterenter image description here
PostController
import Foundation
import UIKit
protocol AnswerhNetworkDelegate: AnyObject {
func getListModels(noteModels: [AnswerModel])
}
class PostController: UIViewController, UITableViewDelegate, UITableViewDataSource, AnswerhNetworkDelegate{
func getListModels(noteModels: [AnswerModel]) {
listModels += noteModels
table.reloadData()
}
private var listModels: [AnswerModel] = []
private var searchText = UITextField()
private var searchButton = UIButton()
private var table = UITableView()
private let answerNetwork = AnswerNetwork()
var model: PostModel = PostModel.empty
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
table.register(PostCell.self, forCellReuseIdentifier: "PostCell")
table.register(CustomAnswerCell.self, forCellReuseIdentifier: "CustomAnswerCell")
table.delegate = self
table.dataSource = self
table.estimatedRowHeight = 68.0
table.rowHeight = UITableView.automaticDimension
setupUI()
answerNetwork.answerNetworkDelegate = self
//переделать
answerNetwork.fetch(idPost: model.id)
}
override func viewDidAppear(_ animated: Bool) {
table.reloadData()
}
private func setupUI() {
table.translatesAutoresizingMaskIntoConstraints = false
table.separatorStyle = .none
view.addSubview(table)
table.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
table.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
table.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
table.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
listModels.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as? PostCell else {
fatalError("не CustomCell")
}
// cell.cellView.image.userInteractionEnabled =
let lpgr = UITapGestureRecognizer(target: self, action: #selector(PostController.handleTapPress(_:)))
cell.cellView.image.isUserInteractionEnabled = true
cell.cellView.image.addGestureRecognizer(lpgr)
cell.selectionStyle = .none
cell.model = model
return cell
} else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "CustomAnswerCell", for: indexPath) as? CustomAnswerCell else {
fatalError("не CustomCell")
}
cell.selectionStyle = .none
cell.model = listModels[indexPath.row - 1]
return cell
}
}
#objc func handleTapPress(_ sender: Any){
UIApplication.shared.openURL(URL(string: model.link)!)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
extension String {
init?(htmlEncodedString: String) {
guard let data = htmlEncodedString.data(using: .utf8) else {
return nil
}
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
guard let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) else {
return nil
}
self.init(attributedString.string)
}
}
PostCell
import Foundation
import UIKit
class PostCell: UITableViewCell {
var cellView = PostView()
private var selectedButton = UIButton(type: .custom)
var model: PostModel? {
get {
cellView.model
}
set {
cellView.model = newValue ?? PostModel.empty
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .white
setupView()
// print(cellView.frame)
// print(contentView.frame)
// print(self.frame)
}
override func prepareForReuse() {
super.prepareForReuse()
isHidden = false
isSelected = false
isHighlighted = false
self.model = PostModel.empty
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
cellView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(cellView)
// addSubview(cellView)
cellView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor).isActive = true
cellView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor).isActive = true
cellView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
cellView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -5).isActive = true
}
}
PostView is in the PostCell as a cell and is injected with the Model
import Foundation
import UIKit
class PostView: UIView {
private var titleText = UILabel()
private var bodyText = UILabel()
private var nameText = UILabel()
var image = UIImageView()
private var dateText = UILabel()
private var answerCount = UILabel()
var model: PostModel = PostModel.empty {
didSet {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.YYY"
titleText.text = model.title
image.load(urlString: model.image)
nameText.text = "Автор:" + model.name
dateText.text = "Дата: " + formatter.string(from: model.date)
answerCount.text = "Кол-во ответов: " + String(model.answer_count)
bodyText.text = model.body
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .systemBackground
setupTitleText()
setupImage()
let tapGR = UITapGestureRecognizer(target: self, action: #selector(self.imageTapped))
image.addGestureRecognizer(tapGR)
image.isUserInteractionEnabled = true
setupNameText()
setupDateText()
setupAnswerCount()
setupBodyText()
}
#objc func imageTapped(sender: UITapGestureRecognizer) {
if sender.state == .ended {
UIApplication.shared.openURL(URL(string: model.link)!)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupTitleText() {
titleText.translatesAutoresizingMaskIntoConstraints = false
titleText.numberOfLines = 0
let maximumLabelSize: CGSize = CGSize(width: 280, height: 9999)
let expectedLabelSize: CGSize = titleText.sizeThatFits(maximumLabelSize)
var newFrame: CGRect = titleText.frame
newFrame.size.height = expectedLabelSize.height
titleText.frame = newFrame
addSubview(titleText)
titleText.topAnchor.constraint(equalTo: topAnchor, constant: 5).isActive = true
titleText.leadingAnchor.constraint( equalTo: leadingAnchor, constant: 10).isActive = true
titleText.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10).isActive = true
}
private func setupImage() {
image.translatesAutoresizingMaskIntoConstraints = false
image.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
addSubview(image)
image.topAnchor.constraint(equalTo: titleText.bottomAnchor, constant: 5).isActive = true
image.leadingAnchor.constraint( equalTo: leadingAnchor, constant: 10).isActive = true
image.trailingAnchor.constraint(equalTo: leadingAnchor, constant: 60).isActive = true
//image.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
}
private func setupNameText() {
nameText.translatesAutoresizingMaskIntoConstraints = false
addSubview(nameText)
nameText.topAnchor.constraint(equalTo: titleText.bottomAnchor, constant: 5).isActive = true
nameText.leadingAnchor.constraint( equalTo: image.trailingAnchor, constant: 10).isActive = true
nameText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
}
private func setupDateText() {
dateText.translatesAutoresizingMaskIntoConstraints = false
addSubview(dateText)
dateText.topAnchor.constraint(equalTo: nameText.bottomAnchor, constant: 5).isActive = true
dateText.leadingAnchor.constraint( equalTo: image.trailingAnchor, constant: 10).isActive = true
dateText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
}
private func setupAnswerCount() {
answerCount.translatesAutoresizingMaskIntoConstraints = false
addSubview(answerCount)
answerCount.topAnchor.constraint(equalTo: dateText.bottomAnchor, constant: 5).isActive = true
answerCount.leadingAnchor.constraint( equalTo: image.trailingAnchor, constant: 10).isActive = true
answerCount.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
// answerCount.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
}
private func setupBodyText() {
bodyText.translatesAutoresizingMaskIntoConstraints = false
bodyText.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bodyText.translatesAutoresizingMaskIntoConstraints = false
bodyText.numberOfLines = 0
let maximumLabelSize: CGSize = CGSize(width: 280, height: 9999)
let expectedLabelSize: CGSize = bodyText.sizeThatFits(maximumLabelSize)
// create a frame that is filled with the UILabel frame data
var newFrame: CGRect = bodyText.frame
// resizing the frame to calculated size
newFrame.size.height = expectedLabelSize.height
// put calculated frame into UILabel frame
bodyText.frame = newFrame
addSubview(bodyText)
bodyText.topAnchor.constraint(equalTo: answerCount.bottomAnchor, constant: 5).isActive = true
bodyText.leadingAnchor.constraint( equalTo: leadingAnchor, constant: 5).isActive = true
bodyText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5).isActive = true
bodyText.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0).isActive = true
}
}

on this github, I looked at how he arranges the constraints and made it for himself https://github.com/jeantimex/ios-swift-collapsible-table-section/blob/master/ios-swift-collapsible-table-section/CollapsibleTableViewCell.swift
and redid PostCell
private func setupView() {
let marginGuide = contentView.layoutMarginsGuide
cellView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(cellView)
cellView.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
cellView.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
cellView.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5).isActive = true
cellView.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
}

Related

Passing Data from one ViewController to Another Programmatically, Swift [duplicate]

This question already has answers here:
How to pass data from modal view controller back when dismissed
(4 answers)
Closed 2 years ago.
I just want to transfer some data between two ViewControllers and also wrote some Code. But, if I update my tableView, the user-given data isn't presented at the tableView. Can anyone help me?
import UIKit
import CoreData
class AddNewRecipeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let reuseIdentifer = "IngredientCell"
let pressGesture = UILongPressGestureRecognizer()
let titleLabel = UILabel()
let tf_one = UITextField()
let tf_two = UITextField()
let tableView = UITableView()
let submitButton = UIButton()
let newButton = UIButton()
var name_array = [String]()
var unit_array = [String]()
var quantity_array = [String]()
let recipeId = Int.random(in: 0...5000)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
createViewControllerItems()
}
#objc func refresh(){
print(name_array)
self.tableView.reloadData()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
func createViewControllerItems() {
// - MARK: Titel
titleLabel.text = "Neues Rezept"
titleLabel.textColor = .black
titleLabel.textAlignment = .center
titleLabel.font = UIFont(name: "Bradley Hand", size: 24)
view.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
titleLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
titleLabel.widthAnchor.constraint(equalToConstant: 200).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 40).isActive = true
newButton.setTitle("test", for: UIControl.State.normal)
newButton.setTitleColor(.black, for: UIControl.State.normal)
newButton.addTarget(self, action: #selector(refresh), for: .touchUpInside)
view.addSubview(newButton)
newButton.translatesAutoresizingMaskIntoConstraints = false
newButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true
newButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
newButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
newButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
// - MARK: Überschrift
tf_one.placeholder = "Name"
tf_one.borderStyle = .roundedRect
tf_one.keyboardType = .default
tf_one.spellCheckingType = .yes
tf_one.smartInsertDeleteType = .yes
tf_one.autocorrectionType = .yes
tf_one.autocapitalizationType = .sentences
view.addSubview(tf_one)
tf_one.translatesAutoresizingMaskIntoConstraints = false
tf_one.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 15).isActive = true
tf_one.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
tf_one.widthAnchor.constraint(equalToConstant: 270).isActive = true
tf_one.heightAnchor.constraint(equalToConstant: 34).isActive = true
// - MARK: Anzahl Portionen
tf_two.placeholder = "Anzahl"
tf_two.borderStyle = .roundedRect
tf_two.keyboardType = .decimalPad
view.addSubview(tf_two)
tf_two.translatesAutoresizingMaskIntoConstraints = false
tf_two.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
tf_two.leftAnchor.constraint(equalTo: tf_one.rightAnchor, constant: 3).isActive = true
tf_two.widthAnchor.constraint(equalToConstant: 70).isActive = true
tf_two.heightAnchor.constraint(equalToConstant: 34).isActive = true
// - MARK: Table View
tableView.delegate = self
tableView.dataSource = self
tableView.register(IngredientCell.self, forCellReuseIdentifier: reuseIdentifer)
tableView.rowHeight = 55
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100).isActive = true
tableView.topAnchor.constraint(equalTo: tf_one.bottomAnchor, constant: 10).isActive = true
// - MARK: Bestätigen
submitButton.layer.cornerRadius = 20
submitButton.clipsToBounds = true
submitButton.backgroundColor = .lightGray
submitButton.setTitle("ok", for: UIControl.State.normal)
submitButton.setTitleColor(.white, for: UIControl.State.normal)
submitButton.titleLabel?.font = UIFont(name: "Chalkduster", size: 24)
submitButton.addTarget(self, action: #selector(save), for: .touchUpInside)
view.addSubview(submitButton)
submitButton.translatesAutoresizingMaskIntoConstraints = false
submitButton.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -30).isActive = true
submitButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
submitButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
submitButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
// - MARK: Add Gesture
pressGesture.addTarget(self, action: #selector(pressAction))
view.addGestureRecognizer(pressGesture)
}
#objc func pressAction() {
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.impactOccurred()
let vc: AddNewIngredientViewController!
vc = AddNewIngredientViewController()
vc.modalPresentationStyle = .automatic
self.present(vc, animated: true)
}
#objc func save() {
//save befor dismiss
dismiss(animated: true, completion: nil)
/*
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let context = appDelegate.persistentContainer.viewContext
let entityName = "Recipes"
guard let newEntity = NSEntityDescription.entity(forEntityName: entityName, in: context) else {
return
}
let newRecipe = NSManagedObject(entity: newEntity, insertInto: context)
let name = tf_one.text
let id = recipeId
let category = 1
let persons = 2
newRecipe.setValue(name, forKey: "name")
newRecipe.setValue(id, forKey: "id")
newRecipe.setValue(category, forKey: "category")
newRecipe.setValue(persons, forKey: "persons")
do {
try context.save()
print("Gespeichert!")
let vc: ViewController!
vc = ViewController()
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: false)
} catch {
print(error)
}*/
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(name_array.count)
return name_array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifer, for: indexPath) as! IngredientCell
cell.nameLabel.text = name_array[indexPath.row]
cell.descriptionLabel.text = "text"
cell.personsLabel.text = "text"
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let context = appDelegate.persistentContainer.viewContext
let entityName = "/ENTRY/"
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
do {
let results = try context.fetch(request)
guard results.count > 0 else {
return
}
if let deleteEntity = results[indexPath.row] as? NSManagedObject {
context.delete(deleteEntity) //Das Entity ist hier nur markiert als gelöscht, für evt arbeiten am Item
if context.hasChanges {
do {
try context.save()
print("Datensatz gelöscht!")
let vc: ViewController!
vc = ViewController()
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: false)
} catch {
print(error)
}
}
}
} catch {
print(error)
}
}
}
}
import UIKit
import CoreData
class AddNewIngredientViewController: UIViewController {
let viewExample = UIView()
let titleLabel = UILabel()
let tf_one = UITextField()
let unitTicker = UISegmentedControl()
let unitLabel = UILabel()
let slider = UISlider()
let submitButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white.withAlphaComponent(0)
createViewControllerItems()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
func createViewControllerItems() {
// - MARK: View
viewExample.backgroundColor = .white
viewExample.layer.cornerRadius = 15
viewExample.clipsToBounds = true
view.addSubview(viewExample)
viewExample.translatesAutoresizingMaskIntoConstraints = false
viewExample.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -200).isActive = true
viewExample.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewExample.widthAnchor.constraint(equalToConstant: 330).isActive = true
viewExample.heightAnchor.constraint(equalToConstant: 350).isActive = true
// - MARK: Titel
titleLabel.text = "Zutat hinzufügen"
titleLabel.textColor = .black
titleLabel.textAlignment = .center
titleLabel.font = UIFont(name: "Chalkduster", size: 24)
view.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
titleLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
titleLabel.widthAnchor.constraint(equalToConstant: 250).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 40).isActive = true
// - MARK: Überschrift
tf_one.placeholder = "Name"
tf_one.borderStyle = .roundedRect
tf_one.keyboardType = .default
tf_one.spellCheckingType = .yes
tf_one.smartInsertDeleteType = .yes
tf_one.autocorrectionType = .yes
tf_one.autocapitalizationType = .sentences
view.addSubview(tf_one)
tf_one.translatesAutoresizingMaskIntoConstraints = false
tf_one.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
tf_one.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
tf_one.widthAnchor.constraint(equalToConstant: 310).isActive = true
tf_one.heightAnchor.constraint(equalToConstant: 34).isActive = true
// - MARK: Unit Ticker
unitTicker.insertSegment(withTitle: "ml", at: 0, animated: true)
unitTicker.insertSegment(withTitle: "mg", at: 1, animated: true)
unitTicker.insertSegment(withTitle: "unit", at: 2, animated: true)
unitTicker.addTarget(self, action: #selector(updateLabel), for: UIControl.Event.allEvents)
view.addSubview(unitTicker)
unitTicker.translatesAutoresizingMaskIntoConstraints = false
unitTicker.topAnchor.constraint(equalTo: view.topAnchor, constant: 150).isActive = true
unitTicker.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// - MARK: Slider
slider.maximumValue = 10
slider.addTarget(self, action: #selector(updateLabel), for: UIControl.Event.allEvents)
view.addSubview(slider)
slider.translatesAutoresizingMaskIntoConstraints = false
slider.topAnchor.constraint(equalTo: view.topAnchor, constant: 200).isActive = true
slider.widthAnchor.constraint(equalToConstant: 310).isActive = true
slider.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// - MARK: Bestätigen
submitButton.layer.cornerRadius = 20
submitButton.clipsToBounds = true
submitButton.backgroundColor = .lightGray
submitButton.setTitle("ok", for: UIControl.State.normal)
submitButton.titleLabel?.font = UIFont(name: "Chalkduster", size: 24)
submitButton.addTarget(self, action: #selector(save), for: .touchUpInside)
view.addSubview(submitButton)
submitButton.translatesAutoresizingMaskIntoConstraints = false
submitButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 100).isActive = true
submitButton.topAnchor.constraint(equalTo: slider.bottomAnchor, constant: 50).isActive = true
submitButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
submitButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
}
#objc func updateLabel() {
unitLabel.textAlignment = .center
if unitTicker.selectedSegmentIndex == 0{
unitLabel.text = "\(Int(slider.value)) ml"
slider.maximumValue = 1000
}else if unitTicker.selectedSegmentIndex == 1{
unitLabel.text = "\(Int(slider.value)) mg"
slider.maximumValue = 1000
}else if unitTicker.selectedSegmentIndex == 2{
unitLabel.text = "\(Int(slider.value)) unit"
slider.maximumValue = 10
}
view.addSubview(unitLabel)
unitLabel.translatesAutoresizingMaskIntoConstraints = false
unitLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 230).isActive = true
unitLabel.widthAnchor.constraint(equalToConstant: 310).isActive = true
unitLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
#objc func save() {
let viewController = AddNewRecipeViewController()
viewController.name_array.append(tf_one.text!)
print(viewController.name_array)
dismiss(animated: true, completion: nil)
}
}
Thanks!
Tom
You can pass data between two viewController by using the delegate method or NSNotificaton. For more info visit about NSNotification: https://developer.apple.com/documentation/foundation/nsnotification
For delegate method you can check https://medium.com/#jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef

Struggling to adjust cell height with increase in content

I have been struggling greatly almost regularly to be able to create a cell whose height adjusts with content while trying to do it programatically, some things i am trying are , image shows the problem
use below function
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
2)Use this in viewDidLoad
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
Setting bottom and top constraints to equal rather then not equal
Below i paste some code to show my struggle or trying every thing to get the cell to expand with content, which it does. not, can any one please suggest some ways to achieve it
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let cell = restaurantMainTable.dequeueReusableCell(withIdentifier: String(describing: RestaurantMainViewCells.self), for: indexPath) as! RestaurantMainViewCells
cell.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
view.addSubview(cell)
view.addSubview(cell.contentView)
view.addSubview(cell.restaurantName)
view.addSubview(cell.restaurantType)
view.addSubview(cell.restaurantLocation)
view.addSubview(cell.restaurantMiniImage)
view.addSubview(cell.restaurantHeartImage)
cell.restaurantHeartImage.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantMiniImage.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantName.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantLocation.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantType.translatesAutoresizingMaskIntoConstraints = false
//Fonts
let font = UIFont(name: "Rubik-Medium", size: 18)
let fontMetrics = UIFontMetrics(forTextStyle: .body)
let labels = [cell.restaurantName, cell.restaurantLocation, cell.restaurantType]
labels.forEach { label in
label.font = fontMetrics.scaledFont(for: font!)
}
let stackLabels = UIStackView()
view.addSubview(stackLabels)
stackLabels.alignment = .top
stackLabels.distribution = .fill
stackLabels.spacing = 5
stackLabels.axis = .vertical
stackLabels.addArrangedSubview(cell.restaurantName)
stackLabels.addArrangedSubview(cell.restaurantType)
stackLabels.addArrangedSubview(cell.restaurantLocation)
let stackImage = UIStackView()
view.addSubview(stackImage)
stackLabels.translatesAutoresizingMaskIntoConstraints = false
stackImage.alignment = .top
stackImage.distribution = .fill
stackImage.axis = .horizontal
stackImage.spacing = 5
cell.restaurantMiniImage.heightAnchor.constraint(equalToConstant: 60).isActive = true
cell.restaurantMiniImage.widthAnchor.constraint(equalToConstant: 60).isActive = true
cell.restaurantMiniImage.layer.cornerRadius = 30
cell.restaurantMiniImage.clipsToBounds = true
cell.restaurantHeartImage.heightAnchor.constraint(equalToConstant: 20).isActive = true
cell.restaurantHeartImage.widthAnchor.constraint(equalToConstant: 20).isActive = true
cell.restaurantHeartImage.trailingAnchor.constraint(equalTo: cell.trailingAnchor, constant: -10).isActive = true
cell.restaurantHeartImage.topAnchor.constraint(equalTo: cell.topAnchor, constant: 20).isActive = true
stackImage.addArrangedSubview(cell.restaurantMiniImage)
stackImage.addArrangedSubview(stackLabels)
view.addSubview(stackImage)
stackImage.translatesAutoresizingMaskIntoConstraints = false
stackImage.leadingAnchor.constraint(equalTo: cell.leadingAnchor, constant: 10).isActive = true
stackImage.topAnchor.constraint(greaterThanOrEqualTo: cell.topAnchor, constant: 10).isActive = true
// stackImage.topAnchor.constraint(equalTo: cell.topAnchor, constant: 10).isActive = true
stackImage.trailingAnchor.constraint(equalTo: cell.restaurantHeartImage.leadingAnchor, constant: -10).isActive = true
// stackImage.bottomAnchor.constraint(equalTo: cell.bottomAnchor, constant: -10).isActive = true
stackImage.bottomAnchor.constraint(greaterThanOrEqualTo: cell.bottomAnchor, constant: -10).isActive = true
cell.restaurantName.text = restaurants[indexPath.row].name
cell.restaurantType.text = restaurants[indexPath.row].type
cell.restaurantLocation.text = restaurants[indexPath.row].location
cell.restaurantHeartImage.image = UIImage(named: "heart-tick")
if let restaurantImage = restaurants[indexPath.row].image {
cell.restaurantMiniImage.image = UIImage(data: restaurantImage as Data)
}
return cell
default:
fatalError("no data found")
}
}
UPDATE - The whole class
//
// RestaurantMainController.swift
// LaVivaRepeat
//
//
import UIKit
import CoreData
class RestaurantMainController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
var restaurants: [Restaurant] = []
var fetchResultController: NSFetchedResultsController<Restaurant>!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return restaurants.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
let restaurantMainTable = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(restaurantMainTable)
//MARK:- add delegates as self, always, else no contact with model will take place
restaurantMainTable.rowHeight = UITableView.automaticDimension
restaurantMainTable.estimatedRowHeight = 60
self.restaurantMainTable.delegate = self
self.restaurantMainTable.dataSource = self
self.restaurantMainTable.separatorStyle = .singleLine
//MARK:- create a view to show when no records are there
let backGroundView = UIView()
view.addSubview(backGroundView)
backGroundView.heightAnchor.constraint(equalToConstant: 500).isActive = true
let backGroundImage = UIImageView()
backGroundImage.translatesAutoresizingMaskIntoConstraints = false
backGroundView.translatesAutoresizingMaskIntoConstraints = false
backGroundImage.heightAnchor.constraint(equalToConstant: 300).isActive = true
backGroundImage.widthAnchor.constraint(equalToConstant: 320).isActive = true
backGroundImage.image = UIImage(named: "empty")
backGroundView.addSubview(backGroundImage)
backGroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
backGroundView.topAnchor.constraint(equalTo: view.topAnchor, constant: 90).isActive = true
backGroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 20).isActive = true
restaurantMainTable.backgroundView = backGroundView
restaurantMainTable.backgroundView?.isHidden = true
//MARK:- Add constraints to table
self.restaurantMainTable.translatesAutoresizingMaskIntoConstraints = false
self.restaurantMainTable.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
self.restaurantMainTable.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
self.restaurantMainTable.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
self.restaurantMainTable.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
//MARK:- register RestaurantMainViewCells
self.restaurantMainTable.register(RestaurantMainViewCells.self, forCellReuseIdentifier: String(describing: RestaurantMainViewCells.self))
//MARK:- Get fetch request
let fetchRequest: NSFetchRequest<Restaurant> = Restaurant.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do {
try fetchResultController.performFetch()
if let fetchObject = fetchResultController.fetchedObjects {
restaurants = fetchObject
}
}
catch {
print(error)
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
restaurantMainTable.rowHeight = UITableView.automaticDimension
switch indexPath.row {
case 0:
let cell = restaurantMainTable.dequeueReusableCell(withIdentifier: String(describing: RestaurantMainViewCells.self), for: indexPath) as! RestaurantMainViewCells
// cell.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
view.addSubview(cell)
view.addSubview(cell.contentView)
view.addSubview(cell.restaurantName)
view.addSubview(cell.restaurantType)
view.addSubview(cell.restaurantLocation)
view.addSubview(cell.restaurantMiniImage)
view.addSubview(cell.restaurantHeartImage)
cell.restaurantHeartImage.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantMiniImage.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantName.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantLocation.translatesAutoresizingMaskIntoConstraints = false
cell.restaurantType.translatesAutoresizingMaskIntoConstraints = false
//Fonts
let font = UIFont(name: "Rubik-Medium", size: 18)
let fontMetrics = UIFontMetrics(forTextStyle: .body)
let labels = [cell.restaurantName, cell.restaurantLocation, cell.restaurantType]
labels.forEach { label in
label.font = fontMetrics.scaledFont(for: font!)
}
let stackLabels = UIStackView()
view.addSubview(stackLabels)
stackLabels.alignment = .top
stackLabels.distribution = .fill
stackLabels.spacing = 5
stackLabels.axis = .vertical
stackLabels.addArrangedSubview(cell.restaurantName)
stackLabels.addArrangedSubview(cell.restaurantType)
stackLabels.addArrangedSubview(cell.restaurantLocation)
let stackImage = UIStackView()
view.addSubview(stackImage)
stackLabels.translatesAutoresizingMaskIntoConstraints = false
stackImage.alignment = .top
stackImage.distribution = .fill
stackImage.axis = .horizontal
stackImage.spacing = 5
cell.restaurantMiniImage.heightAnchor.constraint(equalToConstant: 60).isActive = true
cell.restaurantMiniImage.widthAnchor.constraint(equalToConstant: 60).isActive = true
cell.restaurantMiniImage.layer.cornerRadius = 30
cell.restaurantMiniImage.clipsToBounds = true
cell.restaurantHeartImage.heightAnchor.constraint(equalToConstant: 20).isActive = true
cell.restaurantHeartImage.widthAnchor.constraint(equalToConstant: 20).isActive = true
cell.restaurantHeartImage.trailingAnchor.constraint(equalTo: cell.trailingAnchor, constant: -10).isActive = true
cell.restaurantHeartImage.topAnchor.constraint(equalTo: cell.topAnchor, constant: 20).isActive = true
stackImage.addArrangedSubview(cell.restaurantMiniImage)
stackImage.addArrangedSubview(stackLabels)
view.addSubview(stackImage)
stackImage.translatesAutoresizingMaskIntoConstraints = false
stackImage.leadingAnchor.constraint(equalTo: cell.leadingAnchor, constant: 10).isActive = true
stackImage.topAnchor.constraint(greaterThanOrEqualTo: cell.topAnchor, constant: 10).isActive = true
// stackImage.topAnchor.constraint(equalTo: cell.topAnchor, constant: 10).isActive = true
stackImage.trailingAnchor.constraint(equalTo: cell.restaurantHeartImage.leadingAnchor, constant: -10).isActive = true
// stackImage.bottomAnchor.constraint(equalTo: cell.bottomAnchor, constant: -10).isActive = true
stackImage.bottomAnchor.constraint(greaterThanOrEqualTo: cell.bottomAnchor, constant: -10).isActive = true
cell.restaurantName.text = restaurants[indexPath.row].name
cell.restaurantType.text = restaurants[indexPath.row].type
cell.restaurantLocation.text = restaurants[indexPath.row].location
cell.restaurantHeartImage.image = UIImage(named: "heart-tick")
if let restaurantImage = restaurants[indexPath.row].image {
cell.restaurantMiniImage.image = UIImage(data: restaurantImage as Data)
}
return cell
default:
fatalError("no data found")
}
}
//MARK:- Make custom navigation bar large font size and use rubik fonts
override func viewWillAppear(_ animated: Bool) {
navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationBar.topItem?.title = "LaViva Hotel"
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.hidesBarsOnSwipe = true
if let customFont = UIFont(name: "Rubik-Medium", size: 40) {
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(red: 200/255, green: 70/255, blue: 70/255, alpha: 1), NSAttributedString.Key.font: customFont]
}
//MARK:- for empty table
if restaurants.count > 0 {
self.restaurantMainTable.backgroundView?.isHidden = true
self.restaurantMainTable.separatorStyle = .singleLine
}
else {
self.restaurantMainTable.backgroundView?.isHidden = false
self.restaurantMainTable.separatorStyle = .none
}
//MARK:- make an + button appear on top left
let addButton = UIBarButtonItem(image: UIImage(named: "plus"), style: .plain, target: self, action: #selector(addNewRestaurant))
//navigationController?.navigationItem.rightBarButtonItem = addButton
self.navigationItem.rightBarButtonItem = addButton
}
//MARK:- addNewRestaurant function
#objc func addNewRestaurant() {
let pushController = RestaurantAddController()
navigationController?.pushViewController(pushController, animated: true)
}
//MARK:- try and show cell and tower as default or dark done here
override var preferredStatusBarStyle: UIStatusBarStyle {
return .default
}
//add update delete
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
restaurantMainTable.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
if let newIndexPath = newIndexPath {
restaurantMainTable.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath {
restaurantMainTable.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath {
restaurantMainTable.reloadRows(at: [indexPath], with: .fade)
}
default:
restaurantMainTable.reloadData()
}
if let fetchedObjects = controller.fetchedObjects {
restaurants = fetchedObjects as! [Restaurant]
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
restaurantMainTable.endUpdates()
}
//MARK:- left swipr delete
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (action, view, completionHandler) in
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
let restaurantsToDelete = self.fetchResultController.object(at: indexPath)
context.delete(restaurantsToDelete)
appDelegate.saveContext()
}
completionHandler(true)
}
let swipeConfiguration: UISwipeActionsConfiguration
swipeConfiguration = UISwipeActionsConfiguration(actions: [deleteAction])
return swipeConfiguration
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
UPDATE
import UIKit
class RestaurantMainViewCells: UITableViewCell {
var restaurantMiniImage = UIImageView()
var restaurantHeartImage = UIImageView()
var restaurantName = UILabel()
var restaurantType = UILabel()
var restaurantLocation = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
All of your cell setup - adding and constraining UI elements - should be done in your cell class. Absolutely NOT in cellForRowAt.
You would do well to go through a few tutorials on creating dynamic cells.
But, to give you an idea, here is your code modified so you can see what's happening:
struct Restaurant {
var name: String = ""
var type: String = ""
var location: String = ""
// however you have your image information stored
//var image
}
class RestaurantMainViewCells: UITableViewCell {
var restaurantMiniImage = UIImageView()
var restaurantHeartImage = UIImageView()
var restaurantName = UILabel()
var restaurantType = UILabel()
var restaurantLocation = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func commonInit() -> Void {
// so we can see the image view frames without actual images...
restaurantMiniImage.backgroundColor = .green
restaurantHeartImage.backgroundColor = .red
var font: UIFont = UIFont.systemFont(ofSize: 18)
if let f = UIFont(name: "Rubik-Medium", size: 18) {
font = f
}
let fontMetrics = UIFontMetrics(forTextStyle: .body)
let labels = [restaurantName, restaurantLocation, restaurantType]
labels.forEach { label in
label.font = fontMetrics.scaledFont(for: font)
// so we can see label frames...
label.backgroundColor = .yellow
}
let stackLabels = UIStackView()
stackLabels.alignment = .fill
stackLabels.distribution = .fill
stackLabels.spacing = 5
stackLabels.axis = .vertical
stackLabels.addArrangedSubview(restaurantName)
stackLabels.addArrangedSubview(restaurantType)
stackLabels.addArrangedSubview(restaurantLocation)
let stackImage = UIStackView()
stackImage.alignment = .top
stackImage.distribution = .fill
stackImage.axis = .horizontal
stackImage.spacing = 5
restaurantMiniImage.layer.cornerRadius = 30
restaurantMiniImage.clipsToBounds = true
stackImage.addArrangedSubview(restaurantMiniImage)
stackImage.addArrangedSubview(stackLabels)
contentView.addSubview(stackImage)
contentView.addSubview(restaurantHeartImage)
restaurantHeartImage.translatesAutoresizingMaskIntoConstraints = false
stackImage.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// mini image 60x60
restaurantMiniImage.heightAnchor.constraint(equalToConstant: 60),
restaurantMiniImage.widthAnchor.constraint(equalToConstant: 60),
// heart image 20 x 20
restaurantHeartImage.heightAnchor.constraint(equalToConstant: 20),
restaurantHeartImage.widthAnchor.constraint(equalToConstant: 20),
// heart image top+20 trailing-10
restaurantHeartImage.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
restaurantHeartImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
// horizontal stack top / leading / bottom and trailinh to heart image
// all with 10-pts "padding"
stackImage.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
stackImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
stackImage.trailingAnchor.constraint(equalTo: restaurantHeartImage.leadingAnchor, constant: -10),
stackImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
])
}
}
class RestaurantMainController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var restaurants: [Restaurant] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return restaurants.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
let restaurantMainTable = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(restaurantMainTable)
//MARK:- add delegates as self, always, else no contact with model will take place
restaurantMainTable.estimatedRowHeight = 60
self.restaurantMainTable.delegate = self
self.restaurantMainTable.dataSource = self
self.restaurantMainTable.separatorStyle = .singleLine
//MARK:- Add constraints to table
self.restaurantMainTable.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
restaurantMainTable.topAnchor.constraint(equalTo: view.topAnchor),
restaurantMainTable.bottomAnchor.constraint(equalTo: view.bottomAnchor),
restaurantMainTable.leadingAnchor.constraint(equalTo: view.leadingAnchor),
restaurantMainTable.trailingAnchor.constraint(equalTo: view.trailingAnchor),
])
//MARK:- register RestaurantMainViewCells
self.restaurantMainTable.register(RestaurantMainViewCells.self, forCellReuseIdentifier: String(describing: RestaurantMainViewCells.self))
//MARK:- Get fetch request
// I don't have your "fetch" data, so I'm just adding a couple restaurants here
restaurants.append(Restaurant(name: "Cafe De Loir", type: "Chinese Cousine", location: "Hong Kong"))
restaurants.append(Restaurant(name: "Bob's Cafe", type: "Japanese Cousine", location: "Tokyo"))
restaurants.append(Restaurant(name: "Mary's Restaurant", type: "Home Cooking", location: "Dallas, Texas"))
// let fetchRequest: NSFetchRequest<Restaurant> = Restaurant.fetchRequest()
// let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
// fetchRequest.sortDescriptors = [sortDescriptor]
//
// if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
// let context = appDelegate.persistentContainer.viewContext
// fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
// fetchResultController.delegate = self
//
// do {
// try fetchResultController.performFetch()
// if let fetchObject = fetchResultController.fetchedObjects {
// restaurants = fetchObject
// }
// }
//
// catch {
// print(error)
// }
// }
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: RestaurantMainViewCells.self), for: indexPath) as! RestaurantMainViewCells
let r = restaurants[indexPath.row]
cell.restaurantName.text = r.name
cell.restaurantType.text = r.type
cell.restaurantLocation.text = r.location
//if let restaurantImage = restaurants[indexPath.row].image {
// cell.restaurantMiniImage.image = UIImage(data: restaurantImage as Data)
//}
cell.restaurantHeartImage.image = UIImage(named: "heart-tick")
return cell
}
}
The result (I don't have your images so the image views have green or red background color):

iOS UIButtons in StackView aren't being tapped

I have buttons inside a ButtonView class, to add some background and a label. These ButtonViews are added to a UIStackView which is a view in the PlayOverlay Class. PlayOverlay serves as a parent class to different kinds of overlays, in this example I have only included the BeginOverlay.
BeginOverlay is presented by the PlaySecVC. The Buttons in the BeginOverlay can't be tapped for some reason. I have tried the UIDebugging in XCode to see if there are any views in front of them, and there aren't. They are the frontmost views. I do get one error When UIDebugging that tells me that ButtonView's width, height, and x and y are ambiguous. This is because i have no constraints on it, as shown below, since they are laid out the stack view. How can I make these buttons tappable?
ViewController:
import UIKit
fileprivate struct scvc {
static let overlayWidth: CGFloat = 330
static let overlayFromCenter: CGFloat = 25
static let hotspotSize: CGFloat = 30
static let detailHeight: CGFloat = 214
static let detailWidth: CGFloat = 500
static let arrowMargin: CGFloat = 9
static let arrowSize: CGFloat = 56
static let zoomRect: CGFloat = 200
static let overlayHeight: CGFloat = 267
}
enum playState {
case play
case shuffle
case favorites
}
protocol PlaySec: class {
}
class PlaySecVC: UIViewController, PlaySec {
// MARK: UIComponents
lazy var scrollView: UIScrollView = {
let _scrollView = UIScrollView(frame: .zero)
_scrollView.translatesAutoresizingMaskIntoConstraints = false
_scrollView.clipsToBounds = false
//_scrollView.isUserInteractionEnabled = true
return _scrollView
}()
lazy var imageView: UIImageView = {
let _imageView = UIImageView(frame: .zero)
_imageView.translatesAutoresizingMaskIntoConstraints = false
_imageView.contentMode = .scaleAspectFit
//_imageView.isUserInteractionEnabled = true
return _imageView
}()
lazy var beginOverlay: BeginOverlay = {
let _beginOverlay = BeginOverlay(frame: .zero)
_beginOverlay.translatesAutoresizingMaskIntoConstraints = false
return _beginOverlay
}()
lazy var detailView: UIView = {
let _detailView = UIView(frame: .zero)
_detailView.translatesAutoresizingMaskIntoConstraints = false
_detailView.isHidden = true
//_detailView.isUserInteractionEnabled = false
return _detailView
}()
lazy var leftArrow: UIButton = {
let _leftArrow = UIButton(frame: .zero)
_leftArrow.translatesAutoresizingMaskIntoConstraints = false
_leftArrow.isHidden = false
_leftArrow.setImage(#imageLiteral(resourceName: "Left-Arrow-Outline"), for: .normal)
return _leftArrow
}()
lazy var rightArrow: UIButton = {
let _rightArrow = UIButton(frame: .zero)
_rightArrow.translatesAutoresizingMaskIntoConstraints = false
_rightArrow.isHidden = false
_rightArrow.setImage(#imageLiteral(resourceName: "Right-Arrow-Outline"), for: .normal)
return _rightArrow
}()
var state: playState = .play
// MARK: Setup
private func setup() {
let viewController = self
}
private func setupConstraints() {
view.addSubview(scrollView)
scrollView.addSubview(imageView)
view.addSubview(detailView)
view.addSubview(beginOverlay)
view.addSubview(leftArrow)
view.addSubview(rightArrow)
NSLayoutConstraint.activate([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: scrollView.topAnchor),
imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
beginOverlay.centerXAnchor.constraint(equalTo: view.centerXAnchor),
beginOverlay.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -25),
beginOverlay.widthAnchor.constraint(equalToConstant: scvc.overlayWidth),
beginOverlay.heightAnchor.constraint(equalToConstant: scvc.overlayHeight),
detailView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
detailView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
detailView.heightAnchor.constraint(equalToConstant: scvc.detailHeight),
detailView.widthAnchor.constraint(equalToConstant: scvc.detailWidth),
leftArrow.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: scvc.arrowMargin),
leftArrow.centerYAnchor.constraint(equalTo: view.centerYAnchor),
leftArrow.widthAnchor.constraint(equalToConstant: scvc.arrowSize),
leftArrow.heightAnchor.constraint(equalToConstant: scvc.arrowSize),
rightArrow.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -1 * scvc.arrowMargin),
rightArrow.centerYAnchor.constraint(equalTo: view.centerYAnchor),
rightArrow.widthAnchor.constraint(equalToConstant: scvc.arrowSize),
rightArrow.heightAnchor.constraint(equalToConstant: scvc.arrowSize),
])
}
func favorite() {
}
func play() {
state = .play
}
func favoritesPlay() {
play()
state = .favorites
}
func shufflePlay() {
play()
state = .shuffle
}
override func viewDidLoad() {
super.viewDidLoad()
setup()
setupConstraints()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
/*var touch: UITouch? = touches.first
if (touch?.view != detailView && !detailView.isHidden) {
detailView.isHidden = true
}*/
super.touchesBegan(touches, with: event)
}
}
Overlay:
fileprivate struct sizeConstants {
static let pillHeight: CGFloat = 38
static let pillCornerRadius: CGFloat = sizeConstants.pillHeight / 2
static let titleFontSize: CGFloat = 13
static let detailFontSize: CGFloat = 10
static let imageCenterToLeading: CGFloat = 3
static let circleDiameter: CGFloat = 66
static let circleRadius: CGFloat = sizeConstants.circleDiameter / 2
static let buttonTextHPadding: CGFloat = 4
static let buttonTextVPadding: CGFloat = 2
static let badgeSpacing: CGFloat = 5.5
static let titleBadgeSpacing: CGFloat = 19
static let badgeImageSize: CGFloat = 32
static let badgeTextFromCenter: CGFloat = 0
static let badgeTextToImage: CGFloat = 8
static let buttonBackgroundToText: CGFloat = 6
static let circleButtonSize: CGFloat = 48
static let rectButtonWidth: CGFloat = 36
static let rectButtonHeight: CGFloat = 39
static let badgesToButtons: CGFloat = 21.5
}
class ButtonView: UIView {
lazy var buttonBackgroundView: UIView = {
let _buttonBackgroundView = UIView(frame: .zero)
_buttonBackgroundView.translatesAutoresizingMaskIntoConstraints = false
_buttonBackgroundView.backgroundColor = .black
_buttonBackgroundView.layer.cornerRadius = sizeConstants.circleRadius
return _buttonBackgroundView
}()
lazy var textBackgroundView: UIView = {
let _textBackgroundView = UIView(frame: .zero)
_textBackgroundView.translatesAutoresizingMaskIntoConstraints = false
_textBackgroundView.backgroundColor = .black
_textBackgroundView.layer.cornerRadius = _textBackgroundView.frame.height / 2
return _textBackgroundView
}()
lazy var button: UIButton = {
let _button = UIButton(frame: .zero)
_button.translatesAutoresizingMaskIntoConstraints = false
return _button
}()
lazy var label: UILabel = {
let _label = UILabel(frame: .zero)
_label.translatesAutoresizingMaskIntoConstraints = false
_label.font = .systemFont(ofSize: 15)
_label.textColor = .white
return _label
}()
var isRect: Bool = false
convenience init(rect: Bool) {
self.init(frame: .zero)
self.isRect = rect
setupViews()
}
override func updateConstraints() {
NSLayoutConstraint.activate([
buttonBackgroundView.topAnchor.constraint(equalTo: topAnchor),
buttonBackgroundView.centerXAnchor.constraint(equalTo: centerXAnchor),
buttonBackgroundView.widthAnchor.constraint(equalToConstant: sizeConstants.circleDiameter),
buttonBackgroundView.heightAnchor.constraint(equalToConstant: sizeConstants.circleDiameter),
button.centerXAnchor.constraint(equalTo: buttonBackgroundView.centerXAnchor),
button.centerYAnchor.constraint(equalTo: buttonBackgroundView.centerYAnchor),
textBackgroundView.topAnchor.constraint(equalTo: buttonBackgroundView.bottomAnchor, constant: sizeConstants.buttonBackgroundToText),
textBackgroundView.centerXAnchor.constraint(equalTo: centerXAnchor),
textBackgroundView.heightAnchor.constraint(equalTo: label.heightAnchor, constant: sizeConstants.buttonTextVPadding),
textBackgroundView.widthAnchor.constraint(equalTo: label.widthAnchor, constant: sizeConstants.buttonTextHPadding),
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: textBackgroundView.centerYAnchor),
])
if (isRect) {
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: sizeConstants.rectButtonWidth),
button.heightAnchor.constraint(equalToConstant: sizeConstants.rectButtonHeight),
])
} else {
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: sizeConstants.circleButtonSize),
button.heightAnchor.constraint(equalToConstant: sizeConstants.circleButtonSize),
])
}
super.updateConstraints()
}
private func setupViews() {
addSubview(buttonBackgroundView)
addSubview(textBackgroundView)
addSubview(label)
addSubview(button)
label.sizeToFit()
setNeedsUpdateConstraints()
}
func setButtonProps(image: UIImage, text: String, target: Any, selector: Selector) {
self.button.addTarget(target, action: selector, for: .touchUpInside)
self.button.setImage(image, for: .normal)
self.label.text = text
}
#objc private func tapped() {
print("tapped")
}
}
class PlayOverlay: UIView {
override init(frame: CGRect) {
super.init(frame: .zero)
}
lazy var badgeStackView: UIStackView = {
let _badgeStackView = UIStackView(frame: .zero)
_badgeStackView.translatesAutoresizingMaskIntoConstraints = false
_badgeStackView.axis = .vertical
_badgeStackView.spacing = sizeConstants.badgeSpacing
_badgeStackView.distribution = .equalSpacing
return _badgeStackView
}()
lazy var buttonStackView: UIStackView = {
let _buttonStackView = UIStackView(frame: .zero)
_buttonStackView.translatesAutoresizingMaskIntoConstraints = false
_buttonStackView.axis = .horizontal
_buttonStackView.distribution = .equalSpacing
return _buttonStackView
}()
var vc: PlaySecVC!
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateConstraints() {
NSLayoutConstraint.activate([
badgeStackView.topAnchor.constraint(equalTo: topAnchor, constant: sizeConstants.titleBadgeSpacing),
badgeStackView.centerXAnchor.constraint(equalTo: centerXAnchor),
badgeStackView.widthAnchor.constraint(equalTo: widthAnchor),
buttonStackView.topAnchor.constraint(equalTo: badgeStackView.bottomAnchor, constant: sizeConstants.badgesToButtons),
buttonStackView.widthAnchor.constraint(equalTo: widthAnchor),
buttonStackView.centerXAnchor.constraint(equalTo: centerXAnchor),
buttonStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
super.updateConstraints()
}
}
class BeginOverlay: PlayOverlay {
override init(frame: CGRect) {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
addSubview(badgeStackView)
addSubview(buttonStackView)
let shuffleButton = ButtonView(rect: false)
shuffleButton.setButtonProps(image: UIImage()/* replaced with empty image for demo */, text: "SHUFFLE", target: self, selector: #selector(shuffle))
let favoritesButton = ButtonView(rect: false)
favoritesButton.setButtonProps(image: UIImage()/* replaced with empty image for demo */, text: "FAVORITES", target: self, selector: #selector(favorites))
let playButton = ButtonView(rect: false)
playButton.setButtonProps(image: UIImage()/* replaced with empty image for demo */, text: "PLAY", target: self, selector: #selector(play))
buttonStackView.addArrangedSubview(shuffleButton)
buttonStackView.addArrangedSubview(favoritesButton)
buttonStackView.addArrangedSubview(playButton)
}
#objc private func shuffle() {
vc.shufflePlay()
}
#objc private func favorites() {
vc.favoritesPlay()
}
#objc private func play() {
vc.play()
}
}
I did some research and I figured out that since there are 2 UIStackView inside BeginOverlay, there is position ambiguity for the second one that contains the 3 UIButton. The image below may help.
Here is a place of fix. Tested with Xcode 11.4 / iOS 13.4
lazy var buttonStackView: UIStackView = {
let _buttonStackView = UIStackView(frame: .zero)
_buttonStackView.translatesAutoresizingMaskIntoConstraints = false
_buttonStackView.axis = .horizontal
_buttonStackView.distribution = .fillEqually // << here !!!
return _buttonStackView
}()
Here is complete tested module (for comparison, just in case I changed anything else). Just created single view iOS project from template and assign controller class in Storyboard.
fileprivate struct scvc {
static let overlayWidth: CGFloat = 330
static let overlayFromCenter: CGFloat = 25
static let hotspotSize: CGFloat = 30
static let detailHeight: CGFloat = 214
static let detailWidth: CGFloat = 500
static let arrowMargin: CGFloat = 9
static let arrowSize: CGFloat = 56
static let zoomRect: CGFloat = 200
static let overlayHeight: CGFloat = 267
}
enum playState {
case play
case shuffle
case favorites
}
protocol PlaySec: class {
}
class PlaySecVC: UIViewController, PlaySec {
// MARK: UIComponents
lazy var scrollView: UIScrollView = {
let _scrollView = UIScrollView(frame: .zero)
_scrollView.translatesAutoresizingMaskIntoConstraints = false
_scrollView.clipsToBounds = false
//_scrollView.isUserInteractionEnabled = true
return _scrollView
}()
lazy var imageView: UIImageView = {
let _imageView = UIImageView(frame: .zero)
_imageView.translatesAutoresizingMaskIntoConstraints = false
_imageView.contentMode = .scaleAspectFit
//_imageView.isUserInteractionEnabled = true
return _imageView
}()
lazy var beginOverlay: BeginOverlay = {
let _beginOverlay = BeginOverlay(frame: .zero)
_beginOverlay.translatesAutoresizingMaskIntoConstraints = false
return _beginOverlay
}()
lazy var detailView: UIView = {
let _detailView = UIView(frame: .zero)
_detailView.translatesAutoresizingMaskIntoConstraints = false
_detailView.isHidden = true
//_detailView.isUserInteractionEnabled = false
return _detailView
}()
lazy var leftArrow: UIButton = {
let _leftArrow = UIButton(frame: .zero)
_leftArrow.translatesAutoresizingMaskIntoConstraints = false
_leftArrow.isHidden = false
_leftArrow.setImage(UIImage(systemName: "arrow.left")!, for: .normal)
return _leftArrow
}()
lazy var rightArrow: UIButton = {
let _rightArrow = UIButton(frame: .zero)
_rightArrow.translatesAutoresizingMaskIntoConstraints = false
_rightArrow.isHidden = false
_rightArrow.setImage(UIImage(systemName: "arrow.right")!, for: .normal)
return _rightArrow
}()
var state: playState = .play
// MARK: Setup
private func setup() {
// let viewController = self
self.beginOverlay.vc = self
}
private func setupConstraints() {
view.addSubview(scrollView)
scrollView.addSubview(imageView)
view.addSubview(detailView)
view.addSubview(beginOverlay)
view.addSubview(leftArrow)
view.addSubview(rightArrow)
NSLayoutConstraint.activate([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: scrollView.topAnchor),
imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
beginOverlay.centerXAnchor.constraint(equalTo: view.centerXAnchor),
beginOverlay.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -25),
beginOverlay.widthAnchor.constraint(equalToConstant: scvc.overlayWidth),
beginOverlay.heightAnchor.constraint(equalToConstant: scvc.overlayHeight),
detailView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
detailView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
detailView.heightAnchor.constraint(equalToConstant: scvc.detailHeight),
detailView.widthAnchor.constraint(equalToConstant: scvc.detailWidth),
leftArrow.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: scvc.arrowMargin),
leftArrow.centerYAnchor.constraint(equalTo: view.centerYAnchor),
leftArrow.widthAnchor.constraint(equalToConstant: scvc.arrowSize),
leftArrow.heightAnchor.constraint(equalToConstant: scvc.arrowSize),
rightArrow.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -1 * scvc.arrowMargin),
rightArrow.centerYAnchor.constraint(equalTo: view.centerYAnchor),
rightArrow.widthAnchor.constraint(equalToConstant: scvc.arrowSize),
rightArrow.heightAnchor.constraint(equalToConstant: scvc.arrowSize),
])
}
func favorite() {
}
func play() {
state = .play
}
func favoritesPlay() {
play()
state = .favorites
}
func shufflePlay() {
play()
state = .shuffle
}
override func viewDidLoad() {
super.viewDidLoad()
setup()
setupConstraints()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
/*var touch: UITouch? = touches.first
if (touch?.view != detailView && !detailView.isHidden) {
detailView.isHidden = true
}*/
super.touchesBegan(touches, with: event)
}
}
fileprivate struct sizeConstants {
static let pillHeight: CGFloat = 38
static let pillCornerRadius: CGFloat = sizeConstants.pillHeight / 2
static let titleFontSize: CGFloat = 13
static let detailFontSize: CGFloat = 10
static let imageCenterToLeading: CGFloat = 3
static let circleDiameter: CGFloat = 66
static let circleRadius: CGFloat = sizeConstants.circleDiameter / 2
static let buttonTextHPadding: CGFloat = 4
static let buttonTextVPadding: CGFloat = 2
static let badgeSpacing: CGFloat = 5.5
static let titleBadgeSpacing: CGFloat = 19
static let badgeImageSize: CGFloat = 32
static let badgeTextFromCenter: CGFloat = 0
static let badgeTextToImage: CGFloat = 8
static let buttonBackgroundToText: CGFloat = 6
static let circleButtonSize: CGFloat = 48
static let rectButtonWidth: CGFloat = 36
static let rectButtonHeight: CGFloat = 39
static let badgesToButtons: CGFloat = 21.5
}
class ButtonView: UIView {
lazy var buttonBackgroundView: UIView = {
let _buttonBackgroundView = UIView(frame: .zero)
_buttonBackgroundView.translatesAutoresizingMaskIntoConstraints = false
_buttonBackgroundView.backgroundColor = .black
_buttonBackgroundView.layer.cornerRadius = sizeConstants.circleRadius
return _buttonBackgroundView
}()
lazy var textBackgroundView: UIView = {
let _textBackgroundView = UIView(frame: .zero)
_textBackgroundView.translatesAutoresizingMaskIntoConstraints = false
_textBackgroundView.backgroundColor = .black
_textBackgroundView.layer.cornerRadius = _textBackgroundView.frame.height / 2
return _textBackgroundView
}()
lazy var button: UIButton = {
let _button = UIButton(frame: .zero)
_button.translatesAutoresizingMaskIntoConstraints = false
return _button
}()
lazy var label: UILabel = {
let _label = UILabel(frame: .zero)
_label.translatesAutoresizingMaskIntoConstraints = false
_label.font = .systemFont(ofSize: 15)
_label.textColor = .white
return _label
}()
var isRect: Bool = false
convenience init(rect: Bool) {
self.init(frame: .zero)
self.isRect = rect
setupViews()
}
override func updateConstraints() {
NSLayoutConstraint.activate([
buttonBackgroundView.topAnchor.constraint(equalTo: topAnchor),
buttonBackgroundView.centerXAnchor.constraint(equalTo: centerXAnchor),
buttonBackgroundView.widthAnchor.constraint(equalToConstant: sizeConstants.circleDiameter),
buttonBackgroundView.heightAnchor.constraint(equalToConstant: sizeConstants.circleDiameter),
button.centerXAnchor.constraint(equalTo: buttonBackgroundView.centerXAnchor),
button.centerYAnchor.constraint(equalTo: buttonBackgroundView.centerYAnchor),
textBackgroundView.topAnchor.constraint(equalTo: buttonBackgroundView.bottomAnchor, constant: sizeConstants.buttonBackgroundToText),
textBackgroundView.centerXAnchor.constraint(equalTo: centerXAnchor),
textBackgroundView.heightAnchor.constraint(equalTo: label.heightAnchor, constant: sizeConstants.buttonTextVPadding),
textBackgroundView.widthAnchor.constraint(equalTo: label.widthAnchor, constant: sizeConstants.buttonTextHPadding),
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: textBackgroundView.centerYAnchor),
])
if (isRect) {
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: sizeConstants.rectButtonWidth),
button.heightAnchor.constraint(equalToConstant: sizeConstants.rectButtonHeight),
])
} else {
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: sizeConstants.circleButtonSize),
button.heightAnchor.constraint(equalToConstant: sizeConstants.circleButtonSize),
])
}
super.updateConstraints()
}
private func setupViews() {
addSubview(buttonBackgroundView)
addSubview(textBackgroundView)
addSubview(label)
addSubview(button)
label.sizeToFit()
setNeedsUpdateConstraints()
}
func setButtonProps(image: UIImage, text: String, target: Any, selector: Selector) {
self.button.addTarget(target, action: selector, for: .touchUpInside)
self.button.setImage(image, for: .normal)
self.label.text = text
}
#objc private func tapped() {
print("tapped")
}
}
class PlayOverlay: UIView {
override init(frame: CGRect) {
super.init(frame: .zero)
}
lazy var badgeStackView: UIStackView = {
let _badgeStackView = UIStackView(frame: .zero)
_badgeStackView.translatesAutoresizingMaskIntoConstraints = false
_badgeStackView.axis = .vertical
_badgeStackView.spacing = sizeConstants.badgeSpacing
_badgeStackView.distribution = .equalSpacing
return _badgeStackView
}()
lazy var buttonStackView: UIStackView = {
let _buttonStackView = UIStackView(frame: .zero)
_buttonStackView.translatesAutoresizingMaskIntoConstraints = false
_buttonStackView.axis = .horizontal
_buttonStackView.distribution = .fillEqually
return _buttonStackView
}()
var vc: PlaySecVC!
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateConstraints() {
NSLayoutConstraint.activate([
badgeStackView.topAnchor.constraint(equalTo: topAnchor, constant: sizeConstants.titleBadgeSpacing),
badgeStackView.centerXAnchor.constraint(equalTo: centerXAnchor),
badgeStackView.widthAnchor.constraint(equalTo: widthAnchor),
buttonStackView.topAnchor.constraint(equalTo: badgeStackView.bottomAnchor, constant: sizeConstants.badgesToButtons),
buttonStackView.widthAnchor.constraint(equalTo: widthAnchor),
buttonStackView.centerXAnchor.constraint(equalTo: centerXAnchor),
buttonStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
super.updateConstraints()
}
}
class BeginOverlay: PlayOverlay {
override init(frame: CGRect) {
super.init(frame: .zero)
self.setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
addSubview(badgeStackView)
addSubview(buttonStackView)
let shuffleButton = ButtonView(rect: false)
shuffleButton.setButtonProps(image: UIImage(systemName: "shuffle")!/* replaced with empty image for demo */, text: "SHUFFLE", target: self, selector: #selector(shuffle))
let favoritesButton = ButtonView(rect: false)
favoritesButton.setButtonProps(image: UIImage(systemName: "bookmark")!/* replaced with empty image for demo */, text: "FAVORITES", target: self, selector: #selector(favorites))
let playButton = ButtonView(rect: false)
playButton.setButtonProps(image: UIImage(systemName: "play")!/* replaced with empty image for demo */, text: "PLAY", target: self, selector: #selector(play))
buttonStackView.addArrangedSubview(shuffleButton)
buttonStackView.addArrangedSubview(favoritesButton)
buttonStackView.addArrangedSubview(playButton)
}
#objc private func shuffle() {
vc.shufflePlay()
}
#objc private func favorites() {
vc.favoritesPlay()
}
#objc private func play() {
vc.play()
}
}
Note: as mentioned it is better to review all constrains and fix run-time ambiguities.

TableView dynamic height problem inside a Cell

I have a tableView in tableViewCell with dynamic height together with other ui components having dynamic height & proportinal width to the cell. Problem: inner tableViewCells shrinks when rendered & scrolling outer uitableview. Here is the code:
import UIKit
import WebKit
import SVProgressHUD
class DashboardProgramcell: UITableViewCell, UITableViewDataSource, UITableViewDelegate{
Data, models here..
public var icerikModel:WMPIcerikModel?
static let ID:String = "DashboardProgramcell"
static let turCellPair = (tur: TurID.Program, cellId: ID)
var kListData: WMPArrayOfKatilimciModel?
Cell subViews..
//MainContainer
var container: UIView = {
var container = UIView()
container.translatesAutoresizingMaskIntoConstraints = false
return container
}()
//Left side
var leftContainer: UIView = {
var leftContainer = UIView()
leftContainer.translatesAutoresizingMaskIntoConstraints = false
return leftContainer
}()
var gSaat: UILabel = {
var gSaat = UILabel()
gSaat.translatesAutoresizingMaskIntoConstraints = false
gSaat.numberOfLines = 0
gSaat.font = UIFont.systemFont(ofSize: 11)
return gSaat
}()
var sure: UILabel = {
var sure = UILabel()
sure.translatesAutoresizingMaskIntoConstraints = false
sure.numberOfLines = 0
sure.backgroundColor = ColorHelper.mercury
sure.textAlignment = .center
sure.font = UIFont.systemFont(ofSize: 11)
return sure
}()
var ptad: UITextView = {
var ptad = UITextView()
ptad.textColor = .white
ptad.textAlignment = .center
ptad.isUserInteractionEnabled = false
ptad.isScrollEnabled = false
ptad.translatesAutoresizingMaskIntoConstraints = false
ptad.font = UIFont.systemFont(ofSize: 11)
ptad.textContainerInset = UIEdgeInsets.zero
ptad.textContainer.lineFragmentPadding = 0
return ptad
}()
//Right part
var rightContainer: UIView = {
var rightContainer = UIView()
rightContainer.translatesAutoresizingMaskIntoConstraints = false
return rightContainer
}()
var rightTopTextView: UILabel = {
var rightTopTextView = UILabel()
rightTopTextView.translatesAutoresizingMaskIntoConstraints = false
rightTopTextView.numberOfLines = 0
rightTopTextView.lineBreakMode = NSLineBreakMode.byWordWrapping
rightTopTextView.font = UIFont.systemFont(ofSize: 12)
rightTopTextView.backgroundColor = ColorHelper.arzPopup
return rightTopTextView
}()
Here is the inner tableView and its constraints!
var kList: UITableView = {
var kList = UITableView()
kList.contentMode = .scaleAspectFit
kList.bounces = false
kList.bouncesZoom = false
kList.alwaysBounceVertical = false
kList.translatesAutoresizingMaskIntoConstraints = false
kList.tag = KListCell.TAG
kList.rowHeight = UITableView.automaticDimension
kList.estimatedRowHeight = UITableView.automaticDimension
kList.register(KListCell.self, forCellReuseIdentifier: KListCell.ID)
kList.tableFooterView = UIView(frame: .zero)
return kList
}()
var kListHeightConstraint:NSLayoutConstraint?
var rTopTextViewHeightConstraint:NSLayoutConstraint?
Constructor & constraint activation!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.tag = TurID.Program.rawValue
self.selectionStyle = .none
self.addSubview(container)
container.addSubview(leftContainer) //OK
container.addSubview(rightContainer) //OK
leftContainer.addSubview(gSaat) //OK
leftContainer.addSubview(sure) //OK
leftContainer.addSubview(ptad)
rightContainer.addSubview(rightTopTextView)
rightContainer.addSubview(kList)
NSLayoutConstraint.activate([
container.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 2),
container.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -2),
container.topAnchor.constraint(equalTo: self.topAnchor, constant: 2),
container.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -2),
leftContainer.topAnchor.constraint(equalTo: container.topAnchor),
leftContainer.leftAnchor.constraint(equalTo: container.leftAnchor),
leftContainer.bottomAnchor.constraint(equalTo: container.bottomAnchor),
leftContainer.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.30),
leftContainer.heightAnchor.constraint(equalTo: container.heightAnchor),
leftContainer.rightAnchor.constraint(equalTo: rightContainer.leftAnchor),
rightContainer.topAnchor.constraint(equalTo: container.topAnchor),
rightContainer.bottomAnchor.constraint(equalTo: container.bottomAnchor),
rightContainer.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.68),
rightContainer.heightAnchor.constraint(equalTo: container.heightAnchor),
rightContainer.rightAnchor.constraint(equalTo: container.rightAnchor),
gSaat.topAnchor.constraint(equalTo: leftContainer.topAnchor, constant: 4),
gSaat.leftAnchor.constraint(equalTo: leftContainer.leftAnchor, constant: 4),
gSaat.rightAnchor.constraint(equalTo: leftContainer.rightAnchor, constant: -4),
gSaat.bottomAnchor.constraint(equalTo: sure.topAnchor, constant: -2),
sure.leftAnchor.constraint(equalTo: leftContainer.leftAnchor, constant: 4),
sure.rightAnchor.constraint(equalTo: leftContainer.rightAnchor, constant: -4),
sure.bottomAnchor.constraint(equalTo: ptad.topAnchor, constant:-2),
ptad.leftAnchor.constraint(equalTo: leftContainer.leftAnchor, constant: 4),
ptad.rightAnchor.constraint(equalTo: leftContainer.rightAnchor),
ptad.bottomAnchor.constraint(lessThanOrEqualTo: leftContainer.bottomAnchor),
ptad.heightAnchor.constraint(equalToConstant: 16),
rightTopTextView.topAnchor.constraint(equalTo: rightContainer.topAnchor, constant:4),
rightTopTextView.leftAnchor.constraint(equalTo: rightContainer.leftAnchor, constant:4),
rightTopTextView.rightAnchor.constraint(equalTo: rightContainer.rightAnchor, constant:-4),
rightTopTextView.bottomAnchor.constraint(equalTo: kList.topAnchor),
kList.leftAnchor.constraint(equalTo: rightContainer.leftAnchor, constant:2),
kList.rightAnchor.constraint(equalTo: rightContainer.rightAnchor, constant:-2),
kList.bottomAnchor.constraint(equalTo: rightContainer.bottomAnchor, constant:-2)
])
container.addBorders(edges: [.left, .right, .bottom], color: ColorHelper.programBg, width: 1)
kList.dataSource = self
kList.delegate = self
// kListHeightConstraint = kList.heightAnchor.constraint(equalToConstant: 150)
// kListHeightConstraint?.isActive = true
// rTopTextViewHeightConstraint = rightTopTextView.heightAnchor.constraint(equalToConstant: 120)
// rTopTextViewHeightConstraint?.isActive = true
self.rightTopTextView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapItself)))
self.layoutIfNeeded()
}
#objc func tapItself()
{
SVProgressHUD.show()
DispatchQueue.global(qos: .background).async {
SoapService(vc: self.viewControllerForTableView!).KayitDetay(tur: self.icerikModel!.TID!, kayitID: self.icerikModel!.ID!)
}
}
override func layoutSubviews()
{
super.layoutSubviews()
}
public func adjustSubViews()
{
DispatchQueue.main.async {
if let icerik = self.icerikModel {
//Left
self.gSaat.text = icerik.GSaat ?? ""
self.sure.text = icerik.Sure ?? ""
self.ptad.text = icerik.PTAD ?? ""
self.ptad.backgroundColor = self.ptad.text.isEmpty ? UIColor.clear : (self.ptad.text.hasPrefix("Arz") || self.ptad.text.hasPrefix("arz") ? TurID.Arz.tableviewBg() : TurID.Ziyaret.tableviewBg())
//Right
if let ack = icerik.Ack
{
self.rightTopTextView.attributedText = ack.convertHTML().convertHtmlAttributed()
// self.rTopTextViewHeightConstraint?.constant = self.rightTopTextView.contentSize.height
}
else
{
self.rightTopTextView.heightAnchor.constraint(equalToConstant: 0).isActive = true
}
if let KListe = icerik.KListe, KListe.count != 0 {
self.kListData = KListe
self.kList.reloadData()
}
else
{
// self.kListHeightConstraint?.constant = 5
SVProgressHUD.dismissMainUI { }
}
self.layoutIfNeeded()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let _kListData = self.kListData, _kListData.count != 0 {
return _kListData.count
}
return 0
}
var kTVHeight:CGFloat = 0
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let kListCell = tableView.dequeueReusableCell(withIdentifier: KListCell.ID) as! KListCell
if let _kListData = self.kListData , _kListData.count != 0 {
kListCell.katilimciModel = _kListData[indexPath.row]
kListCell.adjustSubViews()
// self.kTVHeight += kListCell.frame.height
if indexPath.row == _kListData.count - 1 && kTVHeight > 10
{
// kListHeightConstraint?.constant = kTVHeight
// rTopTextViewHeightConstraint?.constant = rightTopTextView.contentSize.height
// kTVHeight = 0
}
}
return kListCell
}
//If we got cell to display this func will work!
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if tableView.visibleCells.count == 0 {
SVProgressHUD.dismissMainUI { }
}
}
}//class DashboardProgramcell
class KListCell: UITableViewCell {
var katilimciModel:WMPKatilimciModel?
static let ID:String = "KListCell"
static let TAG = 4001
//MainContainer
var container: UIView = {
var container = UIView()
container.translatesAutoresizingMaskIntoConstraints = false
return container
}()
//Left side
var leftContainer: UIView = {
var leftContainer = UIView()
leftContainer.translatesAutoresizingMaskIntoConstraints = false
return leftContainer
}()
//Right part
var rightContainer: UIView = {
var rightContainer = UIView()
rightContainer.translatesAutoresizingMaskIntoConstraints = false
return rightContainer
}()
var adSoyad: UILabel = {
var adSoyad = UILabel()
adSoyad.translatesAutoresizingMaskIntoConstraints = false
adSoyad.numberOfLines = 0
adSoyad.font = UIFont.systemFont(ofSize: 10)
return adSoyad
}()
var unvan: UILabel = {
var unvan = UILabel()
unvan.translatesAutoresizingMaskIntoConstraints = false
unvan.numberOfLines = 0
unvan.font = UIFont.systemFont(ofSize: 10)
return unvan
}()
var userImage: UIImageView = {
var userImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 12, height: 12))
userImage.translatesAutoresizingMaskIntoConstraints = false
return userImage
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.addSubview(container)
container.addSubview(leftContainer) //OK
container.addSubview(rightContainer) //OK
leftContainer.addSubview(userImage) //OK
rightContainer.addSubview(adSoyad)
rightContainer.addSubview(unvan)
self.applyConstraints()
}
override func layoutSubviews()
{
super.layoutSubviews()
}
func applyConstraints()
{
NSLayoutConstraint.activate([
container.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 2),
container.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -2),
container.topAnchor.constraint(equalTo: self.topAnchor, constant: 2),
container.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -2),
leftContainer.topAnchor.constraint(equalTo: container.topAnchor),
leftContainer.leftAnchor.constraint(equalTo: container.leftAnchor),
leftContainer.bottomAnchor.constraint(equalTo: container.bottomAnchor),
leftContainer.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.12),
leftContainer.heightAnchor.constraint(equalTo: container.heightAnchor),
leftContainer.rightAnchor.constraint(equalTo: rightContainer.leftAnchor),
rightContainer.topAnchor.constraint(equalTo: container.topAnchor),
rightContainer.bottomAnchor.constraint(equalTo: container.bottomAnchor),
rightContainer.widthAnchor.constraint(equalTo: container.widthAnchor, multiplier: 0.86),
rightContainer.heightAnchor.constraint(equalTo: container.heightAnchor),
rightContainer.rightAnchor.constraint(equalTo: container.rightAnchor),
userImage.topAnchor.constraint(equalTo: leftContainer.topAnchor, constant:2),
userImage.heightAnchor.constraint(equalToConstant: 12),
userImage.widthAnchor.constraint(equalToConstant: 12),
userImage.centerXAnchor.constraint(equalTo: leftContainer.centerXAnchor),
userImage.bottomAnchor.constraint(lessThanOrEqualTo: leftContainer.bottomAnchor),
adSoyad.topAnchor.constraint(equalTo: rightContainer.topAnchor, constant: 2),
adSoyad.leftAnchor.constraint(equalTo: rightContainer.leftAnchor, constant: 2),
adSoyad.rightAnchor.constraint(equalTo: rightContainer.rightAnchor, constant: -2),
adSoyad.bottomAnchor.constraint(equalTo: unvan.topAnchor),
unvan.leftAnchor.constraint(equalTo: rightContainer.leftAnchor, constant: 2),
unvan.rightAnchor.constraint(equalTo: rightContainer.rightAnchor, constant: -2),
unvan.bottomAnchor.constraint(equalTo: rightContainer.bottomAnchor, constant: -2),
])
}
public func adjustSubViews ()
{
DispatchQueue.main.async {
if let katilimci = self.katilimciModel {
self.adSoyad.text = katilimci.AD ?? ""
self.unvan.text = katilimci.Ack ?? ""
self.userImage.image = UIImage(named:"userBlack")
}
}
}
}
Firstly change this code:
self.addSubview(container)
to
contentView.addSubview(container)
Views in cell should be added to contentView since it is root layout.

Why dont some of my anchor constraints work while others do for the same item?

I am trying to put anchor constraints on a textField contained within a subView. For the leading and top anchors the anchors work but for the bottom and trailing anchors they dont. Im not sure what it could be, I would like some space between the keyboard and the items in my subview as well as some space between the UITextField and the trailing anchor edge of the screen. Below is the code in question
Layout Code:
func setUpLayout(){
//myView
self.myView.translatesAutoresizingMaskIntoConstraints = false
self.myView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
self.myView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
self.myView.heightAnchor.constraint(equalToConstant: 98).isActive = true
self.myView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 4).isActive = true
//CollectionView
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
self.collectionView.leadingAnchor.constraint(equalTo: self.myView.leadingAnchor, constant: 0).isActive = true
self.collectionView.trailingAnchor.constraint(equalTo: self.myView.trailingAnchor, constant: 0).isActive = true
self.collectionView.bottomAnchor.constraint(equalTo: self.myView.bottomAnchor, constant: 4).isActive = true
self.collectionView.topAnchor.constraint(equalTo: self.searchBar.bottomAnchor, constant: 4).isActive = true
//searchbar
self.searchBar.translatesAutoresizingMaskIntoConstraints = false
self.searchBar.topAnchor.constraint(equalTo: self.myView.topAnchor, constant: 0).isActive = true
self.searchBar.leadingAnchor.constraint(equalTo: self.myView.leadingAnchor, constant: 4).isActive = true
self.searchBar.trailingAnchor.constraint(equalTo: self.myView.trailingAnchor, constant: 4).isActive = true
self.searchBar.heightAnchor.constraint(equalToConstant: 45).isActive = true
searchBar.backgroundColor = .white
searchBar.layer.borderWidth = 2
searchBar.layer.borderColor = UIColor.black.cgColor
//PictureView
self.PictureView.translatesAutoresizingMaskIntoConstraints = false
self.PictureView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
self.PictureView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
self.PictureView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
self.PictureView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
self.PictureView.backgroundColor = .green
}
Code in its entirety
import UIKit
class SearchCollectionViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout, UITextFieldDelegate, UICollectionViewDataSource,UIGestureRecognizerDelegate {
var myView: UIView!
var searchBar: UITextField!
var collectionView: UICollectionView!
var PictureView: UIView!
var genericArray:[String] = ["A","B","C","D","E","F","G","Ab","Abc","za"]
var currentGenericArray:[String] = [String]()
var tagsSelected:[String] = [String]()
let keyboardSlider = KeyboardSlider()
override func viewDidLoad() {
super.viewDidLoad()
keyboardSlider.subscribeToKeyboardNotifications(view: view)
myView = UIView(frame: CGRect(x: 0, y: self.view.frame.height, width: self.view.frame.width, height: self.view.frame.height))
searchBar = UITextField(frame: CGRect(x: 0, y: 0, width: self.myView.frame.width, height: self.myView.frame.height))
PictureView = UIView(frame: self.view.frame)
self.view.addSubview(PictureView)
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
layout.itemSize = CGSize(width: (UIScreen.main.bounds.width-1)/2, height: (UIScreen.main.bounds.width-1)/2)
layout.scrollDirection = .horizontal
self.collectionView = UICollectionView(frame: CGRect(x: 0, y: self.myView.frame.height, width: self.myView.frame.width, height: 100), collectionViewLayout: layout)
collectionView.backgroundColor = .clear
collectionView.contentInset = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
myView.backgroundColor = .clear
collectionView.delegate = self
collectionView.dataSource = self
self.myView.addSubview(collectionView)
self.view.addSubview(myView)
collectionView.register(CollectionCell.self, forCellWithReuseIdentifier: "collectionViewCell")
currentGenericArray = genericArray
searchBar.delegate = self
searchBar.autocorrectionType = .no
searchBar.keyboardType = .default
searchBar.addTarget(self, action: #selector(SearchCollectionViewController.textFieldDidChange), for: .editingChanged)
self.myView.addSubview(searchBar)
let viewTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(viewTapped(gestureRecognizer:)))
viewTapGestureRecognizer.cancelsTouchesInView = false
self.PictureView.addGestureRecognizer(viewTapGestureRecognizer)
self.PictureView.backgroundColor = .purple
self.view.backgroundColor = .green
self.searchBar.becomeFirstResponder()
self.collectionView.allowsSelection = true
setUpLayout()
}
func setUpLayout(){
//myView
self.myView.translatesAutoresizingMaskIntoConstraints = false
self.myView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
self.myView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
self.myView.heightAnchor.constraint(equalToConstant: 98).isActive = true
self.myView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 4).isActive = true
//CollectionView
self.collectionView.translatesAutoresizingMaskIntoConstraints = false
self.collectionView.leadingAnchor.constraint(equalTo: self.myView.leadingAnchor, constant: 0).isActive = true
self.collectionView.trailingAnchor.constraint(equalTo: self.myView.trailingAnchor, constant: 0).isActive = true
self.collectionView.bottomAnchor.constraint(equalTo: self.myView.bottomAnchor, constant: 4).isActive = true
self.collectionView.topAnchor.constraint(equalTo: self.searchBar.bottomAnchor, constant: 4).isActive = true
//searchbar
self.searchBar.translatesAutoresizingMaskIntoConstraints = false
self.searchBar.topAnchor.constraint(equalTo: self.myView.topAnchor, constant: 0).isActive = true
self.searchBar.leadingAnchor.constraint(equalTo: self.myView.leadingAnchor, constant: 4).isActive = true
self.searchBar.trailingAnchor.constraint(equalTo: self.myView.trailingAnchor, constant: 4).isActive = true
self.searchBar.heightAnchor.constraint(equalToConstant: 45).isActive = true
searchBar.backgroundColor = .white
searchBar.layer.borderWidth = 2
searchBar.layer.borderColor = UIColor.black.cgColor
//PictureView
self.PictureView.translatesAutoresizingMaskIntoConstraints = false
self.PictureView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
self.PictureView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
self.PictureView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
self.PictureView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
self.PictureView.backgroundColor = .green
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
keyboardSlider.unsubscribeFromKeyboardNotifications()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
collectionView.reloadData()
return true
}
/// Helper to dismiss keyboard
#objc func didStopEditing() {
}
func textFieldDidEndEditing(_ textField: UITextField) {
UIView.setAnimationCurve(UIViewAnimationCurve.easeInOut)
UIView.animate(withDuration: 0.2) {
self.view.frame.origin.y = 0
}
}
#objc func textFieldDidChange(){
guard(!(searchBar.text?.isEmpty)!) else{
currentGenericArray = genericArray
collectionView.reloadData()
return
}
currentGenericArray = genericArray.filter({letter -> Bool in
if searchBar.text!.count > letter.count{
return false
}
let stringRange = letter.index(letter.startIndex, offsetBy: searchBar.text!.count)
let subword = letter[..<stringRange]
return subword.lowercased().contains(searchBar.text!.lowercased())
})
if currentGenericArray.isEmpty{
print("text being inserted \(searchBar.text!)")
currentGenericArray.append(searchBar.text!)
}
collectionView.reloadData()
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if (touch.view?.isDescendant(of: self.collectionView))!{
return false
}
return true
}
var keyboardIsOpen:Bool = false
#objc func viewTapped(gestureRecognizer:UIGestureRecognizer){
if keyboardIsOpen{
myView.isHidden = true
keyboardIsOpen = !keyboardIsOpen
searchBar.resignFirstResponder()
}
else{
myView.isHidden = false
keyboardIsOpen = !keyboardIsOpen
searchBar.becomeFirstResponder()
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0{
return tagsSelected.count
}
else {
return currentGenericArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! CollectionCell
if indexPath.section == 0{
cell.collectionLabel.text = tagsSelected[indexPath.item]
cell.backgroundColor = .blue
cell.collectionLabel.textColor = .white
}
else if indexPath.section == 1{
cell.backgroundColor = .white
cell.collectionLabel.textColor = UIColor.black
cell.collectionLabel.text = currentGenericArray[indexPath.row]
}
cell.layer.masksToBounds = true
cell.layer.cornerRadius = cell.bounds.width/20
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 6
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.section == 1{\
if(tagsSelected.contains(currentGenericArray[indexPath.item])){
}
tagsSelected.append(currentGenericArray[indexPath.item])
for i in 0...genericArray.count-1{
if(currentGenericArray[indexPath.item] == genericArray[i]){
genericArray.remove(at: i)
break
}
}
currentGenericArray.remove(at: indexPath.item)
searchBar.text = ""
collectionView.reloadData()
if collectionView.numberOfItems(inSection: 1)>0{
collectionView.scrollToItem(at: IndexPath(item: 0, section: 1), at: .right, animated: true)
}
}
else if indexPath.section == 0{
currentGenericArray.append(tagsSelected[indexPath.item])
tagsSelected.remove(at: indexPath.item)
collectionView.reloadData()
}
}
var offsetY:CGFloat = 0
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(SearchCollectionViewController.keyboardFrameChangeNotification(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
#objc func keyboardFrameChangeNotification(notification: Notification) {
if let userInfo = notification.userInfo {
let endFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect
let animationDuration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double ?? 0
let animationCurveRawValue = (userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int) ?? Int(UIViewAnimationOptions.curveEaseInOut.rawValue)
let animationCurve = UIViewAnimationOptions(rawValue: UInt(animationCurveRawValue))
if let _ = endFrame, endFrame!.intersects(self.myView.frame) {
self.offsetY = self.myView.frame.maxY - endFrame!.minY
} else {
if self.offsetY != 0 {
UIView.animate(withDuration: animationDuration, delay: TimeInterval(0), options: animationCurve, animations: {
self.myView.frame.origin.y = self.myView.frame.origin.y + self.offsetY
self.offsetY = 0
}, completion: nil)
}
}
}
}
}
class CollectionCell:UICollectionViewCell{
var collectionLabel: UILabel!
var view:UIView!
override init(frame: CGRect) {
super.init(frame: frame)
collectionLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height))
self.addSubview(collectionLabel)
collectionLabel.textAlignment = .center
collectionLabel.translatesAutoresizingMaskIntoConstraints = false
collectionLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
collectionLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
collectionLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
collectionLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension UIView {
func currentFirstResponder() -> UIResponder? {
if self.isFirstResponder {
return self
}
for view in self.subviews {
if let responder = view.currentFirstResponder() {
return responder
}
}
return nil
}
}
extension Notification.Name{
static let showKeyboard = Notification.Name("showKeyboard")
}
class KeyboardSlider: NSObject {
// variables to hold and process information from the view using this class
weak var view: UIView?
#objc func keyboardWillShow(notification: NSNotification) {
// method to move keyboard up
view?.frame.origin.y = 0 - getKeyboardHeight(notification as Notification)
}
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
// get exact height of keyboard on all devices and convert to float value to return for use
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
return keyboardSize.cgRectValue.height
}
func subscribeToKeyboardNotifications(view: UIView) {
// assigning view to class' counterpart
self.view = view
// when UIKeyboardWillShow do keyboardWillShow function
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
}
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
}
}
For trailing and bottom anchor constraints, it is common that you have to use negative numbers for the constants to achieve what you want.
For example:
searchBar.bottomAnchor.constraint(equalTo: myView.bottomAnchor, constant: 4).isActive = true
Becomes:
searchBar.bottomAnchor.constraint(equalTo: myView.bottomAnchor, constant: -4).isActive = true

Resources