Ok here is my situation:
Chat app that has a custom inputAccessoryView (Overrode of course)
Need to implement reply to chat functionality (Just like whatsapp where user can reply to individual chats)
I'm showing new view, keyBoardReplyView, which is a short snippet of the chat that I am reply to on top of the inputAccessoryView(Adding this programatically using auto-layout)
This new view keyBoardReplyView contains an image view with a close icon
When i tap on this close icon, I want to remove the KeyboardReplyView but unfortunately, the tap gesture does not seem to fire
Note:
I have isUserInteractionEnabled set to true
What I have tried:
I read in a stack post that I must set the parent view user interaction to true and I tried that as well but still does not work
Code of how I am adding my KeyboardReplyView on top of inputAccessoryView:
let keyboardReplyView = KeyboardSupplementaryReplyView()
fileprivate func setupReplyViewForKeyboard(withTitleColor: UIColor){
keyboardReplyView.titleLabel.textColor = withTitleColor
keyboardReplyView.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleReplyMessageClose))
keyboardReplyView.closeImageView.addGestureRecognizer(tapGesture)
inputAccessoryView?.addSubview(keyboardReplyView)
keyboardReplyView.anchor(top: nil, leading: inputAccessoryView?.leadingAnchor, bottom: inputAccessoryView?.topAnchor, trailing: inputAccessoryView?.trailingAnchor, size: .init(width: 0, height: 60))
}
Here is the code to the view:
class KeyboardSupplementaryReplyView: UIView {
let closeImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFit
iv.isUserInteractionEnabled = true
iv.tintColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
let boldConfig = UIImage.SymbolConfiguration(weight: .bold)
let boldBell = UIImage(systemName: "xmark.circle", withConfiguration: boldConfig)
iv.image = boldBell
return iv
}()
let titleLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 1
label.font = .systemFont(ofSize: 14, weight: .bold)
return label
}()
let messageLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 2
label.font = .systemFont(ofSize: 12)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
private func setupViews(){
let visualEffectView = UIVisualEffectView()
visualEffectView.effect = UIBlurEffect(style: .systemMaterialDark)
addSubview(visualEffectView)
visualEffectView.anchor(top: topAnchor, leading: leadingAnchor, bottom: bottomAnchor, trailing: trailingAnchor)
addSubview(closeImageView)
closeImageView.anchor(top: nil, leading: nil, bottom: nil, trailing: trailingAnchor, padding: .init(top: 0, left: 0, bottom: 0, right: 4), size: .init(width: 30, height: 30))
closeImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
//38
addSubview(titleLabel)
titleLabel.text = "Name of user replying to"
titleLabel.anchor(top: topAnchor, leading: leadingAnchor, bottom: nil, trailing: closeImageView.leadingAnchor, padding: .init(top: 4, left: 4, bottom: 0, right: 4))
addSubview(messageLabel)
messageLabel.textColor = UIColor.white.withAlphaComponent(0.7)
messageLabel.text = "This is a long text that is used to test of the view can hold this in the accurate and the right format let us try it out as much as we can"
messageLabel.anchor(top: titleLabel.bottomAnchor, leading: leadingAnchor, bottom: nil, trailing: closeImageView.leadingAnchor, padding: .init(top: 2, left: 4, bottom: 0, right: 4))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Related
Problem
I am trying to do everything programmatically, so I have created this ResultController. For some reason, two of my buttons work but the other three don't work. The program doesn't even notice the touch-up event. I have read many posts on this topic but none of the solutions worked for me.
The two buttons that work are the language button and the swap button, which are embedded in a stack view towards the top of the screen. The 3 buttons that don't work are the soundButton, loopButton, and addFlashcardButton on the result card shown in the image below.
Question
What do I need to do such that these buttons respond to touch events?
Here is the code for this viewController, as well as a picture of the result. I will post the important parts and the whole code for reference. The Buttons are laid out in two functions, setupResultsView() and setupScrollView():
Button Instantiation
var soundButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "speaker.3.fill"), for: .normal)
button.backgroundColor = .white
button.tintColor = .black
button.addTarget(self, action: #selector(soundButtonPressed), for: .touchUpInside)
return button
}()
var loopButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "repeat"), for: .normal)
button.backgroundColor = .white
button.tintColor = .black
button.addTarget(self, action: #selector(loopButtonPressed), for: .touchUpInside)
return button
}()
var addFlashcardButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
button.setImage(UIImage(systemName: "plus"), for: .normal)
button.backgroundColor = K.Colors.purple
button.tintColor = .white
button.roundCorners(cornerRadius: 15)
button.addTarget(self, action: #selector(addFlashcardButtonPressed), for: .touchUpInside)
return button
}()
Selector Functions
#objc func soundButtonPressed() {
print("soundButton")
}
#objc func loopButtonPressed() {
print("loopButton")
}
#objc func addFlashcardButtonPressed() {
print("adding flashcard")
}
Complete Code
import UIKit
class ResultsController: UIViewController {
//MARK: - Info
let cellID = "SoundItOutCell"
//MARK: - Views
var centerTitle: UILabel = {
let label = UILabel(frame: CGRect(x: 10, y: 0, width: 50, height: 40))
label.backgroundColor = K.Colors.purple
label.font = UIFont.boldSystemFont(ofSize: 30)
label.text = "Aura"
label.numberOfLines = 2
label.textColor = .white
label.textAlignment = .center
return label
}()
var languageBarView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.setUnderlineStyle(color: K.Colors.purple)
return view
}()
var langStackView: UIStackView = {
let stackView = UIStackView()
stackView.backgroundColor = .white
stackView.alignment = .center
stackView.distribution = .fillEqually
return stackView
}()
var languageButton: UIButton = {
let button = UIButton()
button.setTitle("Japanese", for: .normal)
button.addTarget(self, action: #selector(languageButtonPressed), for: .touchUpInside)
button.backgroundColor = .white
button.setTitleColor(UIColor.systemBlue, for: .normal)
return button
}()
var swapButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "repeat"), for: .normal)
button.addTarget(self, action: #selector(swapButtonPressed), for: .touchUpInside)
button.tintColor = .black
button.backgroundColor = .white
return button
}()
var englishLabel: UILabel = {
let label = UILabel()
label.text = "English"
label.textColor = .black
label.backgroundColor = .white
label.textAlignment = .center
return label
}()
var textViewBackgroundView = UIView()
var textView: UITextView = {
let textView = UITextView()
textView.returnKeyType = .search
textView.text = "Enter text"
textView.textColor = .lightGray
textView.font = .systemFont(ofSize: 20)
textView.textContainerInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 50)
return textView
}()
var cancelButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "multiply"), for: .normal)
button.backgroundColor = .white
button.tintColor = .black
button.addTarget(self, action: #selector(cancelButtonPressed), for: .touchUpInside)
button.isHidden = true
return button
}()
var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.backgroundColor = K.Colors.lightGrey
return scrollView
}()
var contentView: UIView = {
let view = UIView()
view.backgroundColor = K.Colors.lightGrey
return view
}()
var resultCardView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.roundCorners(cornerRadius: 10)
return view
}()
var topLabel = UILabel()
var bottomLabel = UILabel()
var soundButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "speaker.3.fill"), for: .normal)
button.backgroundColor = .white
button.tintColor = .black
button.addTarget(self, action: #selector(soundButtonPressed), for: .touchUpInside)
return button
}()
var loopButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "repeat"), for: .normal)
button.backgroundColor = .white
button.tintColor = .black
button.addTarget(self, action: #selector(loopButtonPressed), for: .touchUpInside)
return button
}()
var lineDividerView: UIView = {
let view = UIView()
view.backgroundColor = K.Colors.purple
return view
}()
var addFlashcardButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
button.setImage(UIImage(systemName: "plus"), for: .normal)
button.backgroundColor = K.Colors.purple
button.tintColor = .white
button.roundCorners(cornerRadius: 15)
button.addTarget(self, action: #selector(addFlashcardButtonPressed), for: .touchUpInside)
return button
}()
// Background Views
let resultBackgroundView = UIView ()
let addFlashcardBackgroundView = UIView()
//MARK: - Class Functions
override func viewDidLoad() {
super.viewDidLoad()
setupView()
view.backgroundColor = K.Colors.lightGrey
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupShadows()
}
}
extension ResultsController {
//MARK:- View Setup Functions
func setupView() {
self.setupToHideKeyboardOnTapOnView()
setupNavBar()
setupLanguageSelectionView()
setupTextView()
setupScrollView()
}
func setupShadows() {
textViewBackgroundView.setShadow(color: UIColor.black, opacity: 0.3, offset: .init(width: 0, height: 3), radius: 2)
resultBackgroundView.setShadow(color: .black, opacity: 0.3, offset: CGSize(width: 5, height: 5), radius: 2, cornerRadius: 10)
addFlashcardBackgroundView.setShadow(color: .black, opacity: 0.5, offset: CGSize(width: 2, height: 2), radius: 2, cornerRadius: 15)
}
func setupNavBar() {
// Add Center Title
self.navigationItem.titleView = centerTitle
// Add userbutton
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "person"),
style: .plain,
target: self,
action: #selector(profileButtonTapped))
// Make bar color purple, and buttons white
self.navigationController?.navigationBar.tintColor = .white
self.navigationController?.navigationBar.barTintColor = K.Colors.purple
}
func setupLanguageSelectionView() {
// Add Language Bar View
view.addSubview(languageBarView)
languageBarView.anchor(top: view.safeAreaLayoutGuide.topAnchor,
bottom: nil, leading: view.leadingAnchor,
trailing: view.trailingAnchor,
height: 60,
width: nil)
// Add Stack View
languageBarView.addSubview(langStackView)
langStackView.anchor(top: languageBarView.topAnchor,
bottom: languageBarView.bottomAnchor,
leading: languageBarView.leadingAnchor,
trailing: languageBarView.trailingAnchor,
height: nil,
width: nil,
padding: UIEdgeInsets(top: 0, left: 0, bottom: -2, right: 0))
// Add Language Button
langStackView.addArrangedSubview(languageButton)
// Add Swap Button
langStackView.addArrangedSubview(swapButton)
// Add English Label
langStackView.addArrangedSubview(englishLabel)
}
func setupTextView() {
// Add View
view.addSubview(textViewBackgroundView)
textViewBackgroundView.anchor(top: langStackView.bottomAnchor,
bottom: nil,
leading: view.leadingAnchor,
trailing: view.trailingAnchor,
height: 60,
width: nil,
padding: UIEdgeInsets(top: 2, left: 0, bottom: 0, right: 0))
// Add TextView
textView.delegate = self
view.addSubview(textView)
textView.anchor(top: textViewBackgroundView.topAnchor,
bottom: textViewBackgroundView.bottomAnchor,
leading: textViewBackgroundView.leadingAnchor,
trailing: textViewBackgroundView.trailingAnchor,
height: nil,
width: nil,
padding: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0))
// Add X Button
view.addSubview(cancelButton)
cancelButton.anchor(top: textView.topAnchor,
bottom: nil,
leading: nil,
trailing: textView.trailingAnchor,
height: 40,
width: 40,
padding: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0))
}
func setupScrollView() {
// Add scroll view and content view
view.addSubview(scrollView)
view.sendSubviewToBack(scrollView)
scrollView.addSubview(contentView)
// Anchor Scroll View
scrollView.anchorXCenter(top: textView.topAnchor,
bottom: view.safeAreaLayoutGuide.bottomAnchor,
centerAnchor: view.centerXAnchor,
width: view.widthAnchor,
height: nil)
// Anchor Content View
contentView.anchorXCenter(top: scrollView.topAnchor,
bottom: scrollView.bottomAnchor,
centerAnchor: scrollView.centerXAnchor,
width: scrollView.widthAnchor,
height: nil)
// Setup Results Background
contentView.addSubview(resultBackgroundView)
resultBackgroundView.anchorXCenter(top: contentView.topAnchor,
bottom: nil,
centerAnchor: contentView.centerXAnchor,
width: contentView.widthAnchor,
height: nil,
padding: UIEdgeInsets(top: 90, left: 0, bottom: 0, right: 0),
widthOffset: -40)
// Setup Results Card
resultBackgroundView.addSubview(resultCardView)
resultCardView.anchorXCenter(top: resultBackgroundView.topAnchor,
bottom: resultBackgroundView.bottomAnchor,
centerAnchor: resultBackgroundView.centerXAnchor,
width: resultBackgroundView.widthAnchor,
height: nil)
setupResultsView(resultCardView)
}
func setupResultsView(_ resultCardView: UIView) {
let searchInput = "Hello"
let searchOutput = "Yo what's up dog congratulations"
// Configure Top Label
topLabel.attributedText = NSAttributedString(string: searchInput)
topLabel.configureBasedOnInput()
// Configure Bottom Label
bottomLabel.attributedText = NSAttributedString(string: searchOutput)
bottomLabel.configureBasedOnInput()
// Add Flashcard Button to its Background View
addFlashcardBackgroundView.addSubview(addFlashcardButton)
addFlashcardButton.anchor(top: addFlashcardBackgroundView.topAnchor,
bottom: addFlashcardBackgroundView.bottomAnchor,
leading: addFlashcardBackgroundView.leadingAnchor,
trailing: addFlashcardBackgroundView.trailingAnchor,
height: nil,
width: nil)
// Add subviews to Result Card
resultCardView.addSubview(topLabel)
resultCardView.addSubview(bottomLabel)
resultCardView.addSubview(soundButton)
resultCardView.addSubview(loopButton)
resultCardView.addSubview(lineDividerView)
resultCardView.addSubview(addFlashcardBackgroundView)
// Autolayout Constraints For Result Card Subviews
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.topAnchor.constraint(equalTo: resultCardView.topAnchor, constant: 10).isActive = true
topLabel.centerXAnchor.constraint(equalTo: resultCardView.centerXAnchor, constant: 0).isActive = true
topLabel.leadingAnchor.constraint(greaterThanOrEqualTo: resultCardView.leadingAnchor, constant: 10).isActive = true
topLabel.trailingAnchor.constraint(lessThanOrEqualTo: resultCardView.trailingAnchor, constant: -10).isActive = true
lineDividerView.anchor(top: topLabel.bottomAnchor,
bottom: nil, leading: resultCardView.leadingAnchor,
trailing: resultCardView.trailingAnchor,
height: 1,
width: nil,
padding: UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0))
loopButton.anchor(top: lineDividerView.bottomAnchor,
bottom: nil,
leading: nil,
trailing: resultCardView.trailingAnchor,
height: nil,
width: nil,
padding: UIEdgeInsets(top: 10, left: 0, bottom: 0, right: -10))
soundButton.anchor(top: lineDividerView.bottomAnchor,
bottom: nil,
leading: nil,
trailing: loopButton.leadingAnchor,
height: nil,
width: nil,
padding: UIEdgeInsets(top: 10, left: 0, bottom: 0, right: -10))
bottomLabel.translatesAutoresizingMaskIntoConstraints = false
bottomLabel.topAnchor.constraint(equalTo: soundButton.bottomAnchor, constant: 10).isActive = true
bottomLabel.centerXAnchor.constraint(equalTo: resultCardView.centerXAnchor, constant: 0).isActive = true
bottomLabel.leadingAnchor.constraint(greaterThanOrEqualTo: resultCardView.leadingAnchor, constant: 10).isActive = true
bottomLabel.trailingAnchor.constraint(lessThanOrEqualTo: resultCardView.trailingAnchor, constant: -10).isActive = true
addFlashcardBackgroundView.anchor(top: bottomLabel.bottomAnchor,
bottom: resultCardView.bottomAnchor,
leading: nil,
trailing: resultCardView.trailingAnchor,
height: 30,
width: 30,
padding: UIEdgeInsets(top: 10, left: 0, bottom: -10, right: -10))
}
//MARK:- Selector Functions
#objc func profileButtonTapped() {
print(0)
}
#objc func languageButtonPressed() {
print(1)
}
#objc func swapButtonPressed() {
print(2)
}
#objc func cancelButtonPressed() {
textView.text = ""
textView.textColor = .lightGray
textView.text = "Enter text"
cancelButton.isHidden = true
}
#objc func soundButtonPressed() {
print("soundButton")
}
#objc func loopButtonPressed() {
print("loopButton")
}
#objc func addFlashcardButtonPressed() {
print("adding flashcard")
}
}
Output: Circled Buttons are ones that don't work
I have a "View Controller" and a "Main View", now I have a "child view" that creates a custom view that returns a switch and a label related to that, and I add several of these custom views to my Main View. My question : Is there anyway possible to set button target and access these child views with only setting the delegate in parent View? or each of them needs it own delegate?
child View:
import UIKit
class RowView: UIView {
var title: String
var isOn: Bool
init(title: String, isOn: Bool) {
self.title = title
self.isOn = isOn
super.init(frame: .zero)
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let myLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 14)
label.textColor = .black
label.textAlignment = .center
return label
}()
public let mySwitch:UISwitch = {
let mySwitch = UISwitch()
mySwitch.layer.borderColor = UIColor.white.cgColor
mySwitch.layer.borderWidth = 0.8
mySwitch.layer.cornerRadius = 14
// mySwitch.tag = num
return mySwitch
}()
func setupViews() {
myLabel.text = title
mySwitch.isOn = isOn
addSubview(myLabel)
addSubview(mySwitch)
myLabel.anchor(top: topAnchor, leading: leadingAnchor, bottom: nil, trailing: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
mySwitch.anchor(top: nil, leading: nil, bottom: nil, trailing: trailingAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
mySwitch.centerYAnchor.constraint(equalTo: myLabel.centerYAnchor).isActive = true
}
// this is the suggested size for this view
override var intrinsicContentSize: CGSize {
return CGSize(width: 350, height: 31)
}
}
Parent View:
import UIKit
protocol settingDelegate: AnyObject {
func removeAllFavorites()
//func activateNotification(isOn: Bool)
//func allowNotificationAlert(isOn: Bool)
}
class SettingView: UIView {
//MARK: - Properties
var delegate: settingDelegate?
var notifView: UIView?
var alertView: UIView?
let deleteButton: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
button.setTitle("Delete Favorite Quotes", for: .normal)
button.backgroundColor = UIColor.red
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 5
button.titleLabel?.adjustsFontSizeToFitWidth = true // adjust button text to the size
button.titleLabel?.minimumScaleFactor = 0.5 // make it 50% smaller at max
button.contentEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
button.addTarget(self, action: #selector(deleteHandler), for: .touchUpInside)
button.setContentHuggingPriority(UILayoutPriority(1000), for: .horizontal)
return button
}()
//MARK: - Initializers
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Helper Methods
func setupViews() {
backgroundColor = .white
notifView = RowView(title: "Show day's quote as a notification", isOn: false)
alertView = RowView(title: "Ring an alert when notification is played", isOn: false)
guard let notifView = notifView, let alertView = alertView else { return }
let switchStack = UIStackView(arrangedSubviews: [notifView, alertView])
switchStack.axis = .vertical // stackVer.axis = .vertical
switchStack.distribution = .equalCentering
switchStack.alignment = .leading
switchStack.spacing = 20
let stack = UIStackView(arrangedSubviews: [switchStack, deleteButton])
stack.axis = .vertical
stack.distribution = .equalSpacing
stack.alignment = .center
stack.spacing = 20
addSubview(stack)
stack.anchor(top: safeAreaLayoutGuide.topAnchor, leading: leadingAnchor, bottom: safeAreaLayoutGuide.bottomAnchor, trailing: trailingAnchor, paddingTop: 20, paddingLeft: 10, paddingBottom: 20, paddingRight: 10, width: 0, height: 0)
}
#objc func deleteHandler() {
guard let delegate = delegate else { return }
delegate.removeAllFavorites()
}
#objc func switchChanged(mySwitch: UISwitch) {
print("changed")
}
}
You need 1 delegate and differentiate with a tag
notifView = RowView(title: "Show day's quote as a notification", isOn: false)
alertView = RowView(title: "Ring an alert when notification is played", isOn: false)
notifView.tag = 10
alertView.tag = 11
--- you can do
notifView.addGestureRecognizer(UITapGestureRecognizer(target: self.delegate!, action: #selector(self.delegate!.methodClick)))
I'm not using storyboard and I'm doing everything programmatically.
So my problem is that when try to run the app on my phone (This does not happen when I use the simulator in Xcode), and when I use the following method...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let destinationVC = ThirdViewController()
destinationVC.selectedExercise = exercises![indexPath.row]
self.navigationController?.pushViewController(destinationVC, animated: true)
}
...to transition from a UITableView to a UIView, the contents of the View transition normally, but the background freezes for a second before transitioning to the UIView.
What's causing this and how can I fix this?
Here is the code for the viewController that I'm transitioning to.
import UIKit
import RealmSwift
class ThirdViewController: UIViewController, UITextViewDelegate {
let realm = try! Realm()
var stats : Results<WeightSetsReps>?
var weightTextField = UITextField()
var weightLabel = UILabel()
var notesTextView = UITextView()
var repsTextField = UITextField()
var repsLabel = UILabel()
var timerImage = UIImageView()
var nextSet = UIButton()
var nextExcersise = UIButton()
var selectedExercise : Exercises? {
didSet{
loadWsr()
}
}
//MARK: - ViewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
notesTextView.delegate = self
timeClock()
navConAcc()
labelConfig()
setTextFieldConstraints()
setImageViewConstraints()
setTextViewConstraints()
setButtonConstraints()
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
//MARK: - UILabel
func labelConfig(){
weightTextField.placeholder = "Total weight..."
weightTextField.layer.borderWidth = 1
weightTextField.backgroundColor = .white
weightTextField.layer.cornerRadius = 25
weightTextField.layer.borderColor = UIColor.lightGray.cgColor
weightLabel.text = " Weight (lbs): "
weightLabel.textColor = .black
weightTextField.leftView = weightLabel
weightTextField.leftViewMode = .always
repsTextField.placeholder = "Number of Reps..."
repsTextField.layer.borderWidth = 1
repsTextField.backgroundColor = .white
repsTextField.layer.cornerRadius = 25
repsTextField.layer.borderColor = UIColor.lightGray.cgColor
repsLabel.text = " Repetitions: "
repsLabel.textColor = .black
notesTextView.layer.borderWidth = 1
notesTextView.backgroundColor = .white
notesTextView.layer.cornerRadius = 25
notesTextView.layer.borderColor = UIColor.lightGray.cgColor
notesTextView.text = " Notes..."
notesTextView.textColor = UIColor.lightGray
notesTextView.returnKeyType = .done
repsTextField.leftView = repsLabel
repsTextField.leftViewMode = .always
nextSet.layer.borderWidth = 1
nextSet.backgroundColor = .white
nextSet.layer.cornerRadius = 25
nextSet.layer.borderColor = UIColor.lightGray.cgColor
nextSet.setTitle("Next Set", for: .normal)
nextSet.setTitleColor(.black, for: .normal)
nextSet.addTarget(self, action: #selector(addNewSet), for: .touchUpInside)
nextExcersise.layer.borderWidth = 1
nextExcersise.backgroundColor = .white
nextExcersise.layer.cornerRadius = 25
nextExcersise.layer.borderColor = UIColor.lightGray.cgColor
nextExcersise.setTitle("Next Exercise", for: .normal)
nextExcersise.setTitleColor(.black, for: .normal)
[weightTextField, repsTextField, notesTextView, nextSet, nextExcersise].forEach{view.addSubview($0)}
}
//MARK: - TextView Delegates
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.text == " Notes..." {
textView.text = ""
textView.textColor = UIColor.black
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
}
return true
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text == ""{
notesTextView.text = " Notes..."
notesTextView.layer.borderColor = UIColor.lightGray.cgColor
}
}
//MARK: - Dismiss Keyboard Function
#objc func dismissKeyboard(){
view.endEditing(true)
}
//MARK: - TextField Constraints
func setTextFieldConstraints(){
weightTextField.anchor(top: view.safeAreaLayoutGuide.topAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor,padding: .init(top: 20, left: 40, bottom: 0, right: -40), size: .init(width: 0, height: 50))
repsTextField.anchor(top: weightTextField.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 30, left: 40, bottom: 0, right: -40) ,size: .init(width: 0, height: 50))
}
//MARK: - UIButton Functions
#objc func addNewSet(){
print("It Works")
}
//MARK: - UIButton Constraints
func setButtonConstraints(){
nextSet.anchor(top: nil, leading: view.leadingAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, trailing: nil, padding: .init(top: 0, left: 40, bottom: 0, right: -150), size: .init(width: 120, height: 70))
nextExcersise.anchor(top: nil, leading: nextSet.trailingAnchor, bottom: nextSet.bottomAnchor, trailing: view.trailingAnchor, padding: .init(top: 0, left: 85, bottom: 0, right: -40), size: .init(width: 120, height: 70))
}
//MARK: - ImageView Constraints
func setImageViewConstraints(){
timerImage.anchor(top: repsTextField.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 40, left: 0, bottom: 0, right: 0), size: .init(width: 100, height: 100))
}
//MARK: - TextView Constraints
func setTextViewConstraints(){
notesTextView.anchor(top: timerImage.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 40, left: 40, bottom: 0, right: -40), size: .init(width: 100, height: 110))
}
//MARK: - Navigation Bar Setup
func navConAcc(){
navigationItem.title = selectedExercise?.exerciseName
navigationController?.navigationBar.prefersLargeTitles = true
}
//MARK: - Stopwatch
func timeClock(){
let image1 = UIImage(named: "stopwatch")
timerImage = UIImageView(image: image1)
timerImage.contentMode = .scaleAspectFit
self.view.addSubview(timerImage)
}
//MARK: - Load Data
func loadWsr() {
stats = selectedExercise?.wsr.sorted(byKeyPath: "sets", ascending: true)
}
//MARK: - Save Data
func save(wsr : WeightSetsReps){
do {
try realm.write {
realm.add(wsr)
}
} catch {
print("Error saving wsr data \(error)")
}
}
}
extension UIView {
func anchor(top: NSLayoutYAxisAnchor?, leading: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, trailing: NSLayoutXAxisAnchor?, padding: UIEdgeInsets = .zero, size: CGSize = .zero){
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top, constant: padding.top).isActive = true
}
if let leading = leading {
leadingAnchor.constraint(equalTo: leading, constant: padding.left).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom, constant: padding.bottom).isActive = true
}
if let trailing = trailing {
trailingAnchor.constraint(equalTo: trailing, constant: padding.right).isActive = true
}
if size.width != 0 {
widthAnchor.constraint(equalToConstant: size.width).isActive = true
}
if size.height != 0 {
heightAnchor.constraint(equalToConstant: size.height).isActive = true
}
}
}
You're not implementing programmatic view controllers correctly. A programmatically-created view controller does all of its view building in loadView(), not viewDidLoad(). Therefore, add all of the view controller's subviews in loadView() (without calling super.loadView()). Then use viewDidLoad() (with calling super.viewDidLoad()) to do post-view work, like adding timers, notification observers, etc. I suspect the lagging is caused by an incorrectly-configured lifecycle.
It also appears you're relatively new to iOS or Swift development and so I would strongly suggest you do not use extensions, especially on UIView for auto layout. Learn how it all works first before you begin extending things. The process for programmatic auto layout is:
// adjust parameters first, like color, delegate, etc.
someView.translatesAutoresizingMaskIntoConstraints = false // set resizing to false before adding as a subview
view.addSubview(someView) // add as a subview
someView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true // add constraints
So I have a comments feature in my app that users use to communicate. Each comment has a pic, some text,a button that lets you either flag or reply to comments, and the how long ago the comment was posted. As seen in the picture below
However as seen in the picture there is a clear problem. Depending on the amount of text typically anything that spans one line or more. The time label does not show.
import Foundation
import UIKit
import Firebase
protocol CommentCellDelegate: class {
func optionsButtonTapped(cell: CommentCell)
func handleProfileTransition(tapGesture: UITapGestureRecognizer)
}
class CommentCell: UICollectionViewCell {
weak var delegate: CommentCellDelegate? = nil
override var reuseIdentifier : String {
get {
return "cellID"
}
set {
// nothing, because only red is allowed
}
}
var didTapOptionsButtonForCell: ((CommentCell) -> Void)?
weak var comment: CommentGrabbed?{
didSet{
guard let comment = comment else{
return
}
// print("apples")
// textLabel.text = comment.content
//shawn was also here
profileImageView.loadImage(urlString: comment.user.profilePic!)
// print(comment.user.username)
let attributedText = NSMutableAttributedString(string: comment.user.username!, attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 14)])
attributedText.append(NSAttributedString(string: " " + (comment.content), attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)]))
attributedText.append(NSAttributedString(string: "\n\n", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 4)]))
let timeAgoDisplay = comment.creationDate.timeAgoDisplay()
attributedText.append(NSAttributedString(string: timeAgoDisplay, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12), NSAttributedStringKey.foregroundColor: UIColor.gray]))
textView.attributedText = attributedText
}
}
lazy var textView: UITextView = {
let textView = UITextView()
textView.font = UIFont.systemFont(ofSize: 14)
textView.isScrollEnabled = false
textView.textContainer.maximumNumberOfLines = 2
textView.textContainer.lineBreakMode = .byCharWrapping
textView.isEditable = false
return textView
}()
lazy var profileImageView: CustomImageView = {
let iv = CustomImageView()
iv.clipsToBounds = true
iv.isUserInteractionEnabled = true
iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleProfileTransition)))
iv.contentMode = .scaleAspectFill
return iv
}()
lazy var flagButton: UIButton = {
let flagButton = UIButton(type: .system)
flagButton.setImage(#imageLiteral(resourceName: "icons8-Info-64"), for: .normal)
flagButton.addTarget(self, action: #selector(optionsButtonTapped), for: .touchUpInside)
return flagButton
}()
#objc func optionsButtonTapped (){
didTapOptionsButtonForCell?(self)
}
#objc func onOptionsTapped() {
delegate?.optionsButtonTapped(cell: self)
}
#objc func handleProfileTransition(tapGesture: UITapGestureRecognizer){
delegate?.handleProfileTransition(tapGesture: tapGesture)
// print("Tapped image")
}
override init(frame: CGRect){
super.init(frame: frame)
addSubview(textView)
addSubview(profileImageView)
addSubview(flagButton)
textView.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: flagButton.leftAnchor, paddingTop: 4, paddingLeft: 4, paddingBottom: 4, paddingRight: 4, width: 0, height: 0)
profileImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
profileImageView.layer.cornerRadius = 40/2
flagButton.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 40, height: 40)
flagButton.addTarget(self, action: #selector(CommentCell.onOptionsTapped), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I figured it was my constraints but it doesn't seem like it is. Does anyone have any idea why this would happen?
Autosizing works only when maximumNumberOfLines is zero.
textView.textContainer.maximumNumberOfLines = 0
Refer: Docs
Look hear for edits
So I have a comments feature in my app that users use to communicate. Each comment has a pic, some text and button that lets you either flag or reply to comments. As seen in the picture below
However as seen in the picture there is a clear problem. If you add too much text it seems to run right over the button. Is there anyway I can prevent this from happening I think it looks really bad.
Below is my sorce code for reference I'm thinking it might be my constraints but I am not entirely sure.
import Foundation
import UIKit
import Firebase
protocol CommentCellDelegate: class {
func optionsButtonTapped(cell: CommentCell)
func handleProfileTransition(tapGesture: UITapGestureRecognizer)
}
class CommentCell: UICollectionViewCell {
weak var delegate: CommentCellDelegate? = nil
override var reuseIdentifier : String {
get {
return "cellID"
}
set {
// nothing, because only red is allowed
}
}
var didTapOptionsButtonForCell: ((CommentCell) -> Void)?
var comment: CommentGrabbed?{
didSet{
guard let comment = comment else{
return
}
// print("apples")
// textLabel.text = comment.content
//shawn was also here
profileImageView.loadImage(urlString: comment.user.profilePic!)
// print(comment.user.username)
let attributedText = NSMutableAttributedString(string: comment.user.username!, attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 14)])
attributedText.append(NSAttributedString(string: " " + (comment.content), attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)]))
textView.attributedText = attributedText
}
}
let textView: UITextView = {
let textView = UITextView()
textView.font = UIFont.systemFont(ofSize: 14)
textView.isScrollEnabled = false
textView.isEditable = false
return textView
}()
lazy var profileImageView: CustomImageView = {
let iv = CustomImageView()
iv.clipsToBounds = true
iv.isUserInteractionEnabled = true
iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleProfileTransition)))
iv.contentMode = .scaleAspectFill
return iv
}()
lazy var flagButton: UIButton = {
let flagButton = UIButton(type: .system)
flagButton.setImage(#imageLiteral(resourceName: "icons8-Info-64"), for: .normal)
flagButton.addTarget(self, action: #selector(optionsButtonTapped), for: .touchUpInside)
return flagButton
}()
#objc func optionsButtonTapped (){
didTapOptionsButtonForCell?(self)
}
#objc func onOptionsTapped() {
delegate?.optionsButtonTapped(cell: self)
}
#objc func handleProfileTransition(tapGesture: UITapGestureRecognizer){
delegate?.handleProfileTransition(tapGesture: tapGesture)
// print("Tapped image")
}
override init(frame: CGRect){
super.init(frame: frame)
addSubview(textView)
addSubview(profileImageView)
addSubview(flagButton)
textView.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 4, paddingLeft: 4, paddingBottom: 4, paddingRight: 4, width: 0, height: 0)
profileImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
profileImageView.layer.cornerRadius = 40/2
flagButton.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 40, height: 40)
flagButton.addTarget(self, action: #selector(CommentCell.onOptionsTapped), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You need to assign textView's right anchor to the button's left one so they got aligned on sides. This would be the correct layout setup for the textView:
textView.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: flagButton.leftAnchor, paddingTop: 4, paddingLeft: 4, paddingBottom: 4, paddingRight: 4, width: 0, height: 0)