When i try dismiss keyBoard with interactive mode, i have an error for my inputAccessoryView - swift

I've implemented inputAccessoryView with UITextView. I also set collectionView.keyboardDismissMode = .interactive
When i dissmiss keyboard by using interactive mode i have this two error
1) - First responder warning: '<.InputTextView: 0x7f8f54061800; baseClass = UITextView; frame = (8 8; 307 35.5); text = ''; clipsToBounds = YES; gestureRecognizers = ; layer = ; contentOffset: {0, 0}; contentSize: {130, 0}; adjustedContentInset: {0, 0, 0, 0}>' rejected resignFirstResponder when being removed from hierarchy
2) - [UIWindow endDisablingInterfaceAutorotationAnimated:] called on > without matching -beginDisablingInterfaceAutorotation. Ignoring.
Xcode 10 and swift 4
Code For CollectionViewController
lazy var containerView: InputAccesoryView = {
let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 50)
let containerView = InputAccesoryView(frame: frame)
containerView.backgroundColor = UIColor.groupTableViewBackground
containerView.autoresizingMask = .flexibleHeight
containerView.delegate = self
return containerView
}()
override var inputAccessoryView: UIView {
get {
return containerView
}
}
override var canBecomeFirstResponder: Bool {
return true
}
Code for InputAccesoryView
public let inputTextView: InputTextView = {
let textView = InputTextView()
textView.font = UIFont.systemFont(ofSize: 16)
textView.isScrollEnabled = false
textView.textAlignment = .left
textView.layer.cornerRadius = 10
return textView
}()
private lazy var sendButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "send2"), for: .normal)
button.tintColor = .black
button.addTarget(self, action: #selector(handleUploadComment), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureViewComponents()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
return CGSize.zero
}
private func configureViewComponents(){
addSubview(sendButton)
sendButton.setPosition(top: nil, left: nil, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 8, width: 44, height: 0)
sendButton.centerYAnchor.constraint(equalTo: layoutMarginsGuide.centerYAnchor).isActive = true
addSubview(inputTextView)
inputTextView.setPosition(top: topAnchor, left: leftAnchor, bottom: layoutMarginsGuide.bottomAnchor, right: sendButton.leftAnchor, paddingTop: 8, paddingLeft: 8, paddingBottom: 8, paddingRight: 8, width: 0, height: 0)
let separatorView = UIView()
separatorView.backgroundColor = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1)
addSubview(separatorView)
separatorView.setPosition(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0.5)
}
func clearCommentTextView() {
inputTextView.text = nil
inputTextView.placeHolder.isHidden = false
}

Related

Swift UIBarButtonItem transform doesn't work in scrollViewDidScroll

I have a custom nav item
lazy var closeBtn: UIBarButtonItem = {
let closeBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
closeBtn.setBackgroundImage(UIImage.init(named: "icClose")!.withRenderingMode(.alwaysOriginal), for: .normal)
closeBtn.addTarget(self, action: #selector(handleClose), for: .touchUpInside)
let leftBtn = UIBarButtonItem(customView: closeBtn)
leftBtn.imageInsets = UIEdgeInsets(top: -1, left: 10, bottom: 0, right: 0)
return leftBtn
}()
So on scroll, i want to change a scale of this item
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.navigationItem.leftBarButtonItem?.customView?.transform = CGAffineTransform(scaleX: value, y: value)
}
It just doesn't do anything. Fun thing is that the same code works inside UIView.Animate
func scrollViewDidScroll(_ scrollView: UIScrollView) {
UIView.animate(withDuration: 0.2) {
self.navigationItem.leftBarButtonItem?.customView?.transform = CGAffineTransform(scaleX: value, y: value)
}
}
But i want to transform it immediately based on offset value.

How to animate elements of a UIStackView that’s created in a separate function

I'm trying to animate a stackview and its elements upon pressing a UIButton. the problem is that the stackview is created inside a function which is called in viewdidload.
Because the stackview is created inside a function I cannot call it inside the function that manages the animation... I think i'm doing something wrong...
my stackview creation function
fileprivate func variableContent() {
let stackviewButtons = UIStackView(arrangedSubviews: [registerSerialNumberButton, dothislaterButton])
stackviewButtons.axis = .vertical
stackviewButtons.alignment = .fill
stackviewButtons.spacing = 10
let stackview = UIStackView(arrangedSubviews: [registerTitlelabel, registerdescriptionLabel, serialnumberInput, stackviewButtons])
stackview.axis = .vertical
stackview.spacing = 20
stackview.setCustomSpacing(40, after: registerdescriptionLabel)
stackview.setCustomSpacing(100, after: serialnumberInput)
view.addSubview(stackview)
stackview.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
stackview.centerXInSuperview()
stackview.centerYInSuperview()
}
viewdidload
override func viewDidLoad() {
variableContent()
}
You can return the stackview from your function and keep a reference to it as an instance variable. That way you can reuse it later.
class Controller: UIViewController {
var stackView: UIStackView!
fileprivate func variableContent() -> UIStackView {
let stackviewButtons = UIStackView(arrangedSubviews: [registerSerialNumberButton, dothislaterButton])
stackviewButtons.axis = .vertical
stackviewButtons.alignment = .fill
stackviewButtons.spacing = 10
let stackview = UIStackView(arrangedSubviews: [registerTitlelabel, registerdescriptionLabel, serialnumberInput, stackviewButtons])
stackview.axis = .vertical
stackview.spacing = 20
stackview.setCustomSpacing(40, after: registerdescriptionLabel)
stackview.setCustomSpacing(100, after: serialnumberInput)
view.addSubview(stackview)
stackview.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
stackview.centerXInSuperview()
stackview.centerYInSuperview()
return stackview
}
override func viewDidLoad() {
super.viewDidLoad()
self.stackView = variableContent()
}
}
An other option would be to give it a tag (stackview.tag = 42)
and recover it with self.view.viewWithTag(42)
In addition to Marcio's answer, you can also declare the stackView optionally and set it in the function:
class Controller: UIViewController {
var stackview: UIStackView?
fileprivate func variableContent() {
let stackviewButtons = UIStackView(arrangedSubviews: [registerSerialNumberButton, dothislaterButton])
stackviewButtons.axis = .vertical
stackviewButtons.alignment = .fill
stackviewButtons.spacing = 10
self.stackview = UIStackView(arrangedSubviews: [registerTitlelabel, registerdescriptionLabel, serialnumberInput, stackviewButtons])
stackview.axis = .vertical
stackview.spacing = 20
stackview.setCustomSpacing(40, after: registerdescriptionLabel)
stackview.setCustomSpacing(100, after: serialnumberInput)
view.addSubview(stackview)
stackview.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
stackview.centerXInSuperview()
stackview.centerYInSuperview()
}
// Whatever your animating function is:
func animateStackView() {
guard let stackview = self.stackview else { return }
// You now have access to your stackView for the rest of this function.
}
}
Note: For consistency with your question, I kept the name of the UIStackView as stackview in the code, but according to Swift naming conventions, you should name variables in camelcase (stackView)

UILabel text truncating with wrong alignment when I add an imageView

I have a subclassed UILabel that I am going to be using for about 10 different labels of varying length (all on 1 line!) in IB. The text within each of these labels are static.
This subclassed UILabel should display an imageView to the left of the text. This imageView will hold a small icon.
It's close to working, except I'm getting odd truncating & alignment as seen from the screenshot below.
Simulator:
Storyboard setup:
Here's my code:
class ImageMarkLabel: UILabel {
var labelHeight:CGFloat = 0.0
let smileView = UIView()
let smileImageView = UIImageView()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
uiStuff()
}
func uiStuff() {
labelHeight = self.frame.height
smileView.frame = CGRect(x: 0, y: 0, width: labelHeight, height: labelHeight)
smileImageView.image = UIImage(named: "smile")
smileImageView.frame = CGRect(x: 0, y: 0, width: smileView.frame.width, height: smileView.frame.height)
}
func commonInit(){
smileView.addSubview(smileImageView)
self.addSubview(smileView)
}
override func drawText(in rect: CGRect) {
var insets = UIEdgeInsets()
if UIDevice.current.userInterfaceIdiom == .pad {
insets = UIEdgeInsets(top: 0, left: labelHeight + 10.0, bottom: 0, right: labelHeight + 10.0)
}
else {
insets = UIEdgeInsets(top: 0, left: labelHeight + 5.0, bottom: 0, right: labelHeight + 5.0)
}
super.drawText(in: rect.inset(by: insets))
}
}
Regarding Auto Layout - all of the labels are within a vertical stack view.
How can I get the text & imageView to be centered without causing any text truncating?
Change you frame in UIStuff method ,
class ImageMarkLabel: UILabel {
var labelHeight:CGFloat = 0.0
let smileView = UIView()
let smileImageView = UIImageView()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
override func layoutSubviews() {
super.layoutSubviews()
uiStuff()
}
func uiStuff() {
labelHeight = self.frame.height
smileView.frame = CGRect(x: 0, y: 0, width: labelHeight, height: labelHeight)
smileImageView.image = UIImage(named: "smile")
smileImageView.frame = CGRect(x: 0, y: 0, width: smileView.frame.width, height: smileView.frame.height)
var insets = UIEdgeInsets()
if UIDevice.current.userInterfaceIdiom == .pad {
insets = UIEdgeInsets(top: 0, left: (labelHeight + 10.0), bottom: 0, right: (labelHeight + 10.0))
}
else {
insets = UIEdgeInsets(top: 0, left: (labelHeight + 5.0), bottom: 0, right: (labelHeight + 5.0))
}
let newRect = CGRect(origin: self.frame.origin, size: CGSize(width: self.frame.width + 2*insets.left, height: self.frame.height))
self.frame = newRect
}
func commonInit(){
smileView.addSubview(smileImageView)
self.addSubview(smileView)
}
override func drawText(in rect: CGRect) {
super.drawText(in: rect)
}
}
Output:

retrieve information from firebase and set as an image and label in UICollectionView

I am trying to retrieve some data that Is stored in firebase, and allow that Information to be displayed inside of a collection view. Such as profileImageUrl, name label and email label.
I have tried to read the documentation on firebase and apple website, but for some reason, my code returns plain. I don't receive any errors, but my code is not running as expected. When I go to my app's profile view, the only texts that are displayed are the placeholder that I programmatically placed.
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "users")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
let nameLabel: UILabel = {
let label = UILabel()
label.text = "User's Name"
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = GREEN_Theme
return label
}()
let uidLabel: UILabel = {
let label = UILabel()
label.text = "User's uid"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
let emailLabel: UILabel = {
let label = UILabel()
label.text = "User's email"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
func setupView() {
if Auth.auth().currentUser != nil {
guard let uid = Auth.auth().currentUser?.uid else {
return }
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else {
return }
let user = CurrentUser(uid: uid, dictionary: dictionary)
self.uidLabel.text = uid
self.nameLabel.text = user.name
self.emailLabel.text = user.email
self.profileImageView.loadImageUsingCacheWithUrlString(user.profileImageUrl)
}, withCancel: { (err) in
print("attempting to load information")
})
self.addSubview(profileImageView)
self.addSubview(nameLabel)
self.addSubview(emailLabel)
self.addSubview(uidLabel)
profileImageView.anchors(top: topAnchor, topPad: 125, bottom: bottomAnchor, bottomPad: 75, left: leftAnchor, leftPad: 20, right: rightAnchor, rightPad: 250, height: 20, width: 20)
nameLabel.anchors(top: profileImageView.bottomAnchor, topPad: -50, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
emailLabel.anchors(top: nameLabel.bottomAnchor, topPad: -80, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 125, height: 20, width: 20)
uidLabel.anchors(top: emailLabel.bottomAnchor, topPad: -40, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
profileImageView.layer.zPosition = 10
nameLabel.layer.zPosition = 10
emailLabel.layer.zPosition = 10
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.cornerRadius = 50
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.borderColor = UIColor.white.cgColor
profileImageView.layer.masksToBounds = true
}
}
I would really appreciate it if someone could help critique what I have, as well as help show me what the correct implementation would look like. Any and all help would be greatly appreciated
There are several things that are not correct.
your cell should not be the one in charge of downloading or doing any network call, leave that to the viewController or even better a separate handler for all the network calls.
Firebase is asynchronous so by the time your data is downloaded all the views have already been set and displayed, that is why you are only seeing your placeholders.
You have a lot of redundant code for checking optionals, this is not much of a problem but it is not needed.
You should really think about changing all this code and separating the different tasks to different handlers. For example, you could create a separate class that controls all the calls to Firebase and returns the values with completion blocks, from the viewController or the collectionView itself ask the network handler to give you the data that you need, pass this data to the cell and let the cell display the data, the cell should only display the data, not download it or ask for networks requests. I cannot write up a complete example for you but I think that if you go around in google you will find a lot of very good examples and tutorials.
Anyway, just to get you started and so you have a look at the mistakes that you have in your code, here is a quick fix for it. Even if it works, don't take for granted that everything is fixed. You should really refactor all the code and separate the different tasks. Hope this helps at least so you can solve the problem of displaying your downloaded data in the cell.
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "users")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
let nameLabel: UILabel = {
let label = UILabel()
label.text = "User's Name"
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = GREEN_Theme
return label
}()
let uidLabel: UILabel = {
let label = UILabel()
label.text = "User's uid"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
let emailLabel: UILabel = {
let label = UILabel()
label.text = "User's email"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
// Here you add a variable that is observed for SET, once a set happens bind() will be called and the views will be updated.
var currentUser : CurrentUser? {
didSet {
self.bind()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
//...
}
// setupView() should only add the UI appearance, and in this case it is calling firebase and setting currentUser, but it should not do that. This is what you have to refactor.
func setupView() {
self.addSubview(profileImageView)
self.addSubview(nameLabel)
self.addSubview(emailLabel)
self.addSubview(uidLabel)
profileImageView.anchors(top: topAnchor, topPad: 125, bottom: bottomAnchor, bottomPad: 75, left: leftAnchor, leftPad: 20, right: rightAnchor, rightPad: 250, height: 20, width: 20)
nameLabel.anchors(top: profileImageView.bottomAnchor, topPad: -50, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
emailLabel.anchors(top: nameLabel.bottomAnchor, topPad: -80, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 125, height: 20, width: 20)
uidLabel.anchors(top: emailLabel.bottomAnchor, topPad: -40, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
profileImageView.layer.zPosition = 10
nameLabel.layer.zPosition = 10
emailLabel.layer.zPosition = 10
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.cornerRadius = 50
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.borderColor = UIColor.white.cgColor
profileImageView.layer.masksToBounds = true
guard let user = Auth.auth().currentUser else { return }
let uid = user.uid
let reference = Database.database().reference().child("users").child(uid)
reference.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else { return }
self.currentUser = CurrentUser(uid: uid, dictionary: dictionary)
}, withCancel: { (err) in
print("attempting to load information")
})
}
// This is the method that updates the cell views.
func bind() {
self.uidLabel.text = currentUser.uid
self.nameLabel.text = currentUser.name
self.emailLabel.text = currentUser.email
self.profileImageView.loadImageUsingCacheWithUrlString(currentUser.profileImageUrl)
}

UIButton insets bug for multiline attributed title

I want to design a button respecting these rules:
Content horizontally centered
18px margin on both side of the content
10px between image and title
Title will wrap on multiple line if needed
Use attributed title
I have a working solution using:
button.setTitle(title, for: .normal)
Result:
But I can't figure why it's not working if I use:
button.setAttributedTitle(NSAttributedString(string: title), for: .normal)
Result:
It does not respect the 10px and 18px insets. Also note that the problem only occurs when the title becomes multiline.
Here is the playground I used to pinpoint my problem:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
extension UIImage {
class func imageWithView(view: UIView) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
class ActionBarView: UIView {
// MARK: Properties
private var icon: UIImage?
private let stackView = UIStackView()
private let proposeButton = UIButton()
private let declineButton = UIButton()
// MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
designView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: Design
private func designView() {
designIcon()
designStackView()
designProposeButton()
designDeclineButton()
}
private func designIcon() {
let image = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
image.backgroundColor = .magenta
icon = UIImage.imageWithView(view: image)
}
private func designStackView() {
stackView.addArrangedSubview(proposeButton)
stackView.addArrangedSubview(declineButton)
stackView.distribution = .fillEqually
stackView.spacing = 1
addSubview(stackView)
stackView.frame = frame
}
private func designProposeButton() {
design(button: proposeButton,
icon: icon,
title: "Propose a task")
}
private func designDeclineButton() {
design(button: declineButton,
icon: icon,
title: "Decline")
}
private func design(button: UIButton, icon: UIImage?, title: String) {
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
button.titleLabel?.backgroundColor = .green
button.imageView?.backgroundColor = .orange
button.backgroundColor = .white
button.setImage(icon, for: .normal)
// button.setAttributedTitle(NSAttributedString(string: title), for: .normal)
button.setTitle(title, for: .normal)
button.titleLabel?.lineBreakMode = .byWordWrapping
}
}
let view = ActionBarView(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
PlaygroundPage.current.liveView = view