Calling a function that will hide a button from another class - swift - ios

I'm trying to create an IMDB-ish movie application where when the user doesn't login and presses a Skip button, a Favorite (add a movie to favourites) button would dissappear.
The LandingViewController is where the Skip button is and the MovieTableViewCell is where all the data is presented.
What must I do so that the hide action will work inside the function? what's the logic behind this? what am I missing?
Kindly check the below comments in the code. thanks !
import UIKit
class LandingViewController: UIViewController {
#IBOutlet weak var skipButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func didTapSkip(_ sender: Any) {
MovieTableViewCell().hideButton()
}
import UIKit
class MovieTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var ratingLabel: UILabel!
#IBOutlet weak var languageLabel: UILabel!
#IBOutlet weak var releaseYearLabel: UILabel!
#IBOutlet weak var posterImage: UIImageView!
#IBOutlet weak var favoriteButton: UIButton?
override func awakeFromNib() {
super.awakeFromNib()
favoriteButton?.isHidden = true // works here for some reason
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func hideButton() {
favoriteButton?.isHidden = true // doesn't work and If I unwrap it, the app will crash, because it will find the buttton as NIL for some reason
print("hidebutton") // the print works so the function works when the skip is pressed
}

Try using NSUserDefaults
In LandingViewController
#IBAction func didTapSkip(_ sender: Any) {
UserDefaults.standard.set(true, forKey: "didSkip")
}
In MovieTableViewCell
override func awakeFromNib() {
super.awakeFromNib()
if UserDefaults.standard.bool(forKey: "didSkip") {
favoriteButton?.isHidden = true
} else {
favoriteButton?.isHidden = false
}
}

Related

passing data to a specific label in another view controller, depending on the button pressed

I'm just starting out with swift and decided to create a calorie counting app to test my skills in which I am using an Api to get the nutrition data.
Pressing the add breakfast/lunch/dinner segues to a search view controller from which I pass the calories back.
I am using protocol delegate design pattern. I wanted to know how I could set it up so that when I press the add breakfast button, only the breakfast calorie label is updated and when I press add lunch or dinner, their calorie labels are updated accordingly. any help would be greatly appreciated! I posted the codes of my logViewController and SearchViewController
import UIKit
protocol DataDelegate {
func updateLogCalories(str: String?)
}
class SearchViewController: UIViewController,UITextFieldDelegate,CalorieManagerDelegate{
var delagate: DataDelegate?
#IBOutlet weak var searchTF: UITextField!
#IBOutlet weak var calorieLabel: UILabel!
#IBOutlet weak var foodNameLabel: UILabel!
var calorieManager = CalorieManager()
var logCals : String?
override func viewDidLoad() {
super.viewDidLoad()
calorieManager.delegate=self
searchTF.delegate=self
}
#IBAction func searchPressed(_ sender: Any) {
searchTF.endEditing(true)
print(searchTF.text!)
}
#IBAction func addButtonPressed(_ sender: UIButton) {
delagate?.updateLogCalories(str: logCals)
self.dismiss(animated: true, completion: nil)
}
class LogViewController: UIViewController{
var breakfastCal: String?
#IBOutlet weak var breakfastLabel: UILabel!
#IBOutlet weak var lunchLabel: UILabel!
#IBOutlet weak var totalCaloriesLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let navController = segue.destination as! UINavigationController
let destController = navController.topViewController as! SearchViewController
destController.delagate = self
}
#IBAction func addBreakfastPressed(_ sender: UIButton) {
}
#IBAction func addLunchPressed(_ sender: UIButton) {
}
}
extension LogViewController: DataDelegate{
func updateLogCalories(str: String?) {
breakfastLabel.text = str
}
}
If all of your buttons (breakfast, lunch, and dinner) trigger the addButtonPressed action, you need a way to tell which button was pressed, and a way to pass that information to the DataDelegate.
I suggest you put your buttons into an array:
#IBOutlet weak var breakfastButton: UIButton!
#IBOutlet weak var lunchButton: UIButton!
#IBOutlet weak var dinnerButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Populate our array of buttons so we can search for a button
buttons = [breakfastButton, lunchButton, dinnerButton]
}
Then modify your DataDelegate protocol to include a meal enum:
enum Meal: Int {
case breakfast = 0
case lunch = 1
case dinner = 2
}
protocol DataDelegate {
func updateLogCalories(str: String?, forMeal meal: Meal)
}
And set up your DataDelegate to implement the new method:
class MyDataDelegate: DataDelegate {
func updateLogCalories(str: String?, forMeal meal: Meal) {
let str = str ?? ""
print("updating calories with string \(str) for meal \(meal)")
}
}
Now modify your addButtonPressed method so it searches the array to figure out which button was pressed.
#IBAction func addButtonPressed(_ sender: UIButton) {
if let index = buttons.firstIndex(of: sender),
let meal = Meal(rawValue: index) {
print("Button at index \(index) pressed")
delegate.updateLogCalories(str: nil, forMeal: meal)
} else {
print("Can't find button or can't create enum.")
}
}

Better way of handling xib button press than chained delegates?

I have implemented some code including protocols to chain the active instance up to the VC to allow for Segues and am wondering if there's a better way to solve this problem or if I'm handling the Segue the wrong way.
Segues within child classes do not work because they are not in the primary VC
Excerpt from VC file
class ViewController: UIViewController, UITableViewDelegate, TableViewControllerDelegate {
func didButtonPressed() {
performSegue(withIdentifier: "destStory", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
tableViewController.delegate = self
}
}
protocol TableViewControllerDelegate {
func didButtonPressed()
}
class TableViewController: UITableViewController, TableViewCellDelegate {
func didButtonPressed() {
delegate?.didButtonPressed()
}
let test = ["test1", "test2", "test3"]
var instanceOf = self
var delegate: TableViewControllerDelegate?
Excerpt from cell file
protocol TableViewCellDelegate {
func didButtonPressed()
}
class TableViewCell: UITableViewCell {
var delegate: TableViewCellDelegate?
#IBOutlet weak var labelOne: UILabel!
#IBOutlet weak var labelTwo: UILabel!
#IBOutlet weak var buttonOne: UIButton!
#IBAction func buttonOnePressed(_ sender: Any) {
delegate?.didButtonPressed()
}
This functions the way I'd expect but seems potentially confusing to work with.
Another option is using closures instead of delegate.
class TableViewCell: UITableViewCell {
var buttonPressedClosure: (() -> Void))?
#IBOutlet weak var labelOne: UILabel!
#IBOutlet weak var labelTwo: UILabel!
#IBOutlet weak var buttonOne: UIButton!
#IBAction func buttonOnePressed(_ sender: Any) {
buttonPressedClosure?()
}
}
Also, you can add target action for cell's button inside cellForRow UITableViewDelegate function directly, but it is a dirty way in my opinion.
let cell = ...
cell.button.addTarget(self, action: #selector(didButtonPressed), for: .touchUpInside)

Why isn't the data passing between the view controllers? Swift 3

I have 2 text labels on one view controller and I want to connect the text from the labels to the corresponding UIButtons and UILabels on another View Controller.
Right now when I press on the "thisUploadPhoto" button it loads 2 screens of the next screen, instead of just one screen. If you could help me with that it would be great. Also, when I press the done and cancel buttons on the PhotoLabelViewController it gives me this error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[tot.PhotoShareLabelViewController didTapDone:]: unrecognized selector sent to instance 0x7f8cecf5d0b0'
When it is supposed to print 'done'
I don't know why my code below isn't working on connecting the 2 view controllers together.
import UIKit
class PhotoShareViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var contentTextView: UITextView!
#IBOutlet weak var thatTextField: UITextField!
#IBOutlet weak var thisTextField: UITextField!
var presenter: PhotoShareModuleInterface!
var image: UIImage!
#IBAction func thisUploadPhoto(_ sender: Any) {
if thisTextField.text != "" && thatTextField.text != ""
{
performSegue(withIdentifier: "segue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var photoShareLabelViewController = segue.destination as! PhotoShareLabelViewController
photoShareLabelViewController.thisString = thisTextField.text!
photoShareLabelViewController.thatString = thatTextField.text!
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
imageView.image = image
}
override var prefersStatusBarHidden: Bool {
return true
}
#IBAction func didTapCancel(_ sender: AnyObject) {
presenter.cancel()
presenter.pop()
}
#IBAction func didTapDone(_ sender: AnyObject) {
guard let message = thatTextField.text, !message.isEmpty else {
return
}
guard let messageOne = thisTextField.text, !messageOne.isEmpty else {
return
}
presenter.finish(with: image, content:message)
presenter.dismiss()
}
}
extension PhotoShareViewController: PhotoShareViewInterface {
var controller: UIViewController? {
return self
}
}
import UIKit
class PhotoShareLabelViewController: UIViewController {
#IBOutlet weak var thisLabel: UILabel!
#IBOutlet weak var thatLabel: UILabel!
#IBOutlet weak var thisButton: UIButton!
#IBOutlet weak var thatButton: UIButton!
var thisCounter = 0
var thatCounter = 0
var presenter: PhotoShareModuleInterface!
var image: UIImage!
#IBAction func pressedThisButton(_ sender: Any) {
thisCounter += 1
print(thisCounter)
}
#IBAction func pressedThatButton(_ sender: Any) {
thatCounter += 1
print(thatCounter)
}
var thisString = String()
var thatString = String()
#IBAction func pressedButtonDone(_ sender: Any) {
print("done")
}
#IBAction func pressedButtonCancel(_ sender: Any) {
print("cancel")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
thisLabel.text = thisString
thisButton.setTitle(thisString, for: UIControlState.normal)
thatLabel.text = thatString
thatButton.setTitle(thatString, for: UIControlState.normal)
}
}
The "Done" button is wired from Storyboard to call a method called didTapDone in PhotoShareLabelViewController. However, your didTapDone method is implemented in PhotoShareViewController. It's a different view controller.
To fix this, put the method implementation in the correct view controller. Then, select your "Done" button in the Storyboard and go to its "Connections Inspector" [see image below] and remove its current wrong connection. Then, wire it to the correct didTapDone method in the correct view controller.

How to approach putting photos( and labels and buttons) into a Scroll View, having it lengthen every time a new photo is added in Storyboard

Right now on my Storyboard I have a View that I have a segue connecting to a scroll view. I want to make it so that the stuff (an image, label, buttons) on the first View on the Storyboard will go to the Scrollview once the segue button is pressed.
Is there a way that when I segue into the Scroll View the Scroll View will only be the size that it needs to be to fit the newly inputted information from the first View.
Also is there a way that I can save what was put into the Scroll View so the users can add to the scroll view to make it larger. I have Firebase in my app if I need to use that to save the Scroll View information.
Below are my two view controllers (I have no view controller for my scroll view but I can make one if I need it) and a screenshot of my storyboard if that will help!
import UIKit
class PhotoShareViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var contentTextView: UITextView!
#IBOutlet weak var thatTextField: UITextField!
#IBOutlet weak var thisTextField: UITextField!
var presenter: PhotoShareModuleInterface!
var image: UIImage!
#IBAction func thisUploadPhoto(_ sender: Any) {
if thisTextField.text != "" && thatTextField.text != "" {
performSegue(withIdentifier: "segue", sender: nil)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let photoShareLabelViewController = segue.destination as! PhotoShareLabelViewController
photoShareLabelViewController.thisString = thisTextField.text!
photoShareLabelViewController.thatString = thatTextField.text!
photoShareLabelViewController.imageLoaded = image
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
imageView.image = image
}
override var prefersStatusBarHidden: Bool {
return true
}
#IBAction func didTapCancel(_ sender: AnyObject) {
presenter.cancel()
presenter.pop()
}
#IBAction func didTapDone(_ sender: AnyObject) {
guard let message = thatTextField.text, !message.isEmpty else {
return
}
guard let messageOne = thisTextField.text, !messageOne.isEmpty else {
return
}
presenter.finish(with: image, content:message)
presenter.dismiss()
}
}
extension PhotoShareViewController: PhotoShareViewInterface {
var controller: UIViewController? {
return self
}
}
import UIKit
class PhotoShareLabelViewController: UIViewController {
#IBOutlet weak var thisLabel: UILabel!
#IBOutlet weak var thatLabel: UILabel!
#IBOutlet weak var thisButton: UIButton!
#IBOutlet weak var thatButton: UIButton!
#IBOutlet weak var changedImage: UIImageView!
var thisCounter = 0
var thatCounter = 0
#IBAction func pressedDoneButtonLabel(_ sender: Any) {
print("done")
}
var presenter: PhotoShareModuleInterface!
var imageLoaded: UIImage!
#IBAction func pressedThisButton(_ sender: Any) {
thisCounter += 1
print(thisCounter)
}
#IBAction func pressedThatButton(_ sender: Any) {
thatCounter += 1
print(thatCounter)
}
var thisString = String()
var thatString = String()
#IBAction func pressedButtonDone(_ sender: Any) {
print("done")
}
// #IBAction func pressedButtonCancel(_ sender: Any) {
// print("cancel")
// }
override func viewDidLoad() {
super.viewDidLoad()
thisLabel.text = thisString
thisButton.setTitle(thisString, for: UIControlState.normal)
thatLabel.text = thatString
thatButton.setTitle(thatString, for: UIControlState.normal)
changedImage.image = imageLoaded
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Thanks so much! Any and all help is appreciated!
It sounds like what you're trying to do was practically made for a UITableView or UICollectionView. UITableView is actually a subclass of UIScrollView for good reason – the content keeps expanding as needed. You will need to change your data model to be an array of whatever it is you want to display, but that should be fairly easy.
I'm not entirely sure what you're trying to achieve but if I'm correct you have a dynamically changing amount of photos?
If that's the case I would suggest you look at CollectionViews. This will handle the scroll view size for you and help formatting.
Consider these two links
https://developer.apple.com/documentation/uikit/uicollectionview
Great tutorial:
https://www.raywenderlich.com/136159/uicollectionview-tutorial-getting-started

Swift Stepper Action that changes UITextField and UILabel within same cell

I am using a function to build my custom cell. the function used to create the cell is "makeMyList". The stepper I have successfully increments and decrements the itemQuantity textField. The trouble I am having comes with updating the "itemPrice" label. I want itemPrice.text to be quantity * price and update dynamically as the quantity either goes up or down dynamically.
Could someone possibly help me with how to get this to happen?
Thank you in advance!
class MyListTableViewCell: UITableViewCell
{
#IBOutlet weak var itemPrice: UILabel!
#IBOutlet weak var itemName: UILabel!
#IBOutlet weak var itemQuantity: UITextField!
#IBOutlet weak var stepper: UIStepper!
#IBAction func itemQuantityStepper(sender: UIStepper)
{
self.itemQuantity.text = Int(sender.value).description
}
override func awakeFromNib() {
super.awakeFromNib()
}
func makeMyList(myItem: MyList)
{
self.itemName.text = myItem.MyItemName
self.itemPrice.text = String(stepper.value * Double(myItem.MyItemPrice))
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
For anyone who has the same issue. Here is the answer to my own question.
Hope it helps!
class MyListTableViewCell: UITableViewCell
{
#IBOutlet weak var itemPrice: UILabel!
#IBOutlet weak var itemName: UILabel!
#IBOutlet weak var itemQuantity: UITextField!
#IBOutlet weak var stepper: UIStepper!
var price: Float = 0.0
var quantity: Int = 1
override func awakeFromNib() {
super.awakeFromNib()
}
func makeMyList(myItem: MyList)
{
self.itemName.text = myItem.MyItemName
self.itemPrice.text = myItem.MyItemPrice
price = Float(myItem.MyItemPrice)
}
#IBAction func itemQuantityStepper(sender: UIStepper)
{
quantity = Int(sender.value)
self.itemQuantity.text = String(quantity)
self.itemPrice.text = price * Float(quantity)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}

Resources