Line spacing doesn't work - ios

I want to change line spacing for my textLabel in storyboard. I select text -> attributed and change paragraph line to 1. If I use textLabel in storyboard everything works fine. But If I connect textLabel in storyboard with my ViewController, this line spacing doesn't work. How to fix it?
My code:
struct Root : Decodable {
let questions : [Question]
}
struct Question : Decodable {
let number, text, rightAnswer : String
}
class ViewController: UIViewController, UITextFieldDelegate {
var questions = [Question]()
#IBOutlet var textLabel: UILabel!
func updateUI(with question: Question) {
title = question.number
textLabel.text = question.text
showAnswerLabel.text = question.rightAnswer
}
}

You can accomplished that by using a lazy var and some property observers. Configure the text styles the way you want in Interface Builder and use this code:
class ViewController: UIViewController {
#IBOutlet weak var textLabel: UILabel!
lazy var defaultAttributes = textLabel.attributedText!.attributes(at: 0, effectiveRange: nil)
var questionText: String {
get { return textLabel.text! }
set { textLabel.attributedText = NSAttributedString(string: newValue, attributes: defaultAttributes) }
}
override func viewDidLoad() {
super.viewDidLoad()
questionText = "What are the color of yellow cab taxi in New York City?"
}
}
Notce that we did not touch textLabel directly to change its text. We do so via the computed property questionText.
Result:

Related

use array element as var UILabel name in swift

I'm trying to build a key/values pair, for referencing later.
The array will hold a index reference, and it's values inside. One of the values is a video matching the index, and the other value is the corresponding label name (so I can change the color when video is played).
Here is my code:
class ViewController: UIViewController {
var videos = [5: ["urltovideo1", "locationOne"], 7: ["urltovideo2", "locationTwo"]]
var bg: UILabel!
#IBOutlet weak var locationOne: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
bg = videos[5]![1]
bg.backgroundColor = UIColor.green
}
}
I get an error "Cannot assign value of type 'String' to type 'UILabel!'"
I tried "bg = UILabel(videos[5]![1])" and get an error ""Argument labels '(_:)' do not match any available overloads""
Any ideas on how to fix it or better way to implement this?
Here is a solution for you:
#objc var locationOne: UILabel!
#objc var locationTwo: UILabel!
var videos = [5: ["urltovideo1", "locationOne"], 7: ["urltovideo2", "locationTwo"]]
var bg: UILabel! = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
if let bg = self.value(forKey: videos[5]![1]) as? UILabel {
bg.backgroundColor = UIColor.green
}
}
#objc is required to make self.value(forKey:) working properly - you have to add it for every property you want to access via this method.

Setting images of a button

I have come across an issue I cannot resolve.
I have a struct and would like to change the image of a button in each collection view. My text/image works as according to plan. However, when trying to change the image button I am struggling. Could someone please help me?
class CollectionViewContent{
var caption = ""
var mainImage: UIImage!
var userProfileImage : UIButton
init(caption: String, mainImage: UIImage!, userProfileImage : UIButton! )
{
self.description = description
self.mainImage = featuredImage
self.userProfileImage = postedUserProfileImage
}
static func showPosts() -> [Posts]
{
return [
Posts(description: "Sweet", mainImage: UIImage(named: "Image 1"), postedUserProfileImage:!),
Posts(description: "Awesome", mainImage: UIImage(named: "Image 2")!),
Posts(description: "WOW!", mainImage: UIImage(named: "Image 3")!
)
]
}
}
This is my collectionViewCell class
class colViewContetCollectionViewCell: UICollectionViewCell {
var posts: Posts! {
didSet {
updateUI()
}
}
#IBOutlet weak var featuredImage: UIImageView!
#IBOutlet weak var postCaptionLabel: UILabel!
#IBOutlet weak var postedUserProfileImage: UIButton!
func updateUI() {
postCaptionLabel?.text! = posts.title
featuredImage?.image! = posts.featuredImage
postedUserProfileImage = posts.postedUserProfileImage
}
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = 10.0
self.clipsToBounds = true
}
}
I think this line of code is incorrect by the way, I am not too sure.
postedUserProfileImage = posts.postedUserProfileImage
Instead of:
postedUserProfileImage = posts.postedUserProfileImage
You should do:
postedUserProfileImage.setImage(posts.postedUserProfileImage.image(for: UIControlState.normal), for: UIControlState.normal)
Since postedUserProfileImage is a UIButton, you need to use the setImage method to set it's image for a control specific control state. If no images are set for the other control states, the image for the normal state is used for the others too.
And since posts.postedUserProfileImage is also a UIButton (as mentioned in the comments below) you need to get the button's image via the image(for:) method.

Unexpectedly found nil while unwrapping an optional value uitextfield

I know there are a lot of unexpected nil value questions here, but I've tried other approaches and they're not working for me. The function at the bottom is called from a button on another view controller. Basically the issue is that this code works as expected, the form element has the DatePicker as the input view, and it updates startDatePickerField.text when the value is changed, so I know that startDatePickerField.text is not empty, as it is displayed on the screen. However, when calling the testForValidity() function is called from another view controller containing the one we are working in, I get the "unexpectedly found nil while unwrapping option value error". All of the outlets are connected properly so I know it isn't that.
class popoverTableViewController: UITableViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet var startDatePickerField: UITextField!
override func viewDidLoad() {
let startDatePicker:UIDatePicker = UIDatePicker()
startDatePicker.datePickerMode = UIDatePickerMode.dateAndTime
startDatePickerField.inputView = startDatePicker
startDatePicker.minuteInterval = 5
startDatePicker.addTarget(self, action: #selector(popoverTableViewController.startDatePickerValueChanged(_:)), for: UIControlEvents.valueChanged)
}
#IBAction func startDateDidBegin(_ sender: AnyObject) {
}
func startDatePickerValueChanged(_ sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = DateFormatter.Style.long
dateFormatter.timeStyle = DateFormatter.Style.short
startDatePickerField.text = dateFormatter.string(from: sender.date)
finishDateTime = sender.date
}
And the function that errors:
func testForValidity() {
print(startDatePickerField.text)
}
Issue:
You're trying to access IBOutlet property from another class via instance method.
The reason behind the nil value is IBOutlets are initialized when the nib is loaded.
Hence in your case when you are in another view controller you can't access an Outlet variable else you will be always getting nil.
Solution:
If I assume correctly, you want to use a value from a view controller after having a value in it.
The best way is to follow 'Design patterns'. i.e., You have to store your required value in a model class and access the value via model class instance.
Apple has very good documentation about it. Please go through.
https://developer.apple.com/library/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson6.html#//apple_ref/doc/uid/TP40015214-CH20-SW1
Sample Code in Swift:
class TextFieldViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var outputLabel : UILabel!
#IBOutlet weak var textField : UITextField!
override func viewDidLoad() { super.viewDidLoad(); setupTextField() }
func testValidity() -> (textFieldValue: UITextField?, modelValue: String?) {
return (textField, TextFieldModel.sharedModel.textFieldData)
}
//MARK: - Text Field Delegates
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
outputLabel.text = textField.text
TextFieldModel.sharedModel.textFieldData = textField.text
return true
}
}
class TextFieldModel {
static let sharedModel = TextFieldModel()
var textFieldData: String?
}
class DemoController: UIViewController {
#IBOutlet weak var textFieldOutput: UILabel!
#IBOutlet weak var modelOutput: UILabel!
override func viewDidLoad() { super.viewDidLoad(); testValues() }
func testValues() {
let tvc = TextFieldViewController()
textFieldOutput.text = "Value of Text field is " + (tvc.testValidity().textFieldValue?.text ?? "nil")
modelOutput.text = "Value accessed from Model Class is \(tvc.testValidity().modelValue)"
}
}
extension TextFieldViewController {
func setupTextField() {
textField.placeholder = ""
textField.delegate = self
textField.becomeFirstResponder()
textField.keyboardType = UIKeyboardType.Default
textField.returnKeyType = UIReturnKeyType.Done
}
}
Output:
At the top, add UITextFieldDelegate so it becomes:
class popoverTableViewController: UITableViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
Then in viewDidLoad() add this line :
self.startDatePickerField.delegate = self

How do I add a custom action to the text selection edit menu in iOS?

I need to add a custom action to the edit menu that pops up when a user selects some text in a UITextView in iOS.
How do I do this?
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
addCustomMenu()
}
func addCustomMenu() {
let printToConsole = UIMenuItem(title: "Print To Console", action: #selector(printToConsole))
UIMenuController.shared().menuItems = [printToConsole]
}
func printToConsole() {
if let range = textView.selectedTextRange, let selectedText = textView.text(in: range) {
print(selectedText)
}
}
}
This is an example of text selection menu item that changes the text in a UITextView to red. changeToRedFunc can perform any action you want.
Note: This is in Swift 3
(ask if you want it in Swift 2.3)
Hope this helps! If you have any questions feel free to ask! :D
SWIFT 5
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
addCustomMenu()
}
func addCustomMenu() {
//Xcode doesn't like printToConsole being a var and a function call
let printToConsole = UIMenuItem(title: "Print To Console", action: #selector(printToConsole2))
UIMenuController.shared.menuItems = [printToConsole]
}
#objc func printToConsole2() {
if let range = textView.selectedTextRange, let selectedText = textView.text(in: range) {
print(selectedText)
}
}
}
Here is how you create a custom edit menu in swift 5:
import UIKit
class ViewController: UIViewController {
#IBOutlet var textfield: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let changeBackground = UIMenuItem(title: "Change Background Colour", action: #selector(changeBackgroundColour))
UIMenuController.shared.menuItems = [changeBackground] //will add it to everything that has
}
#objc func changeBackgroundColour()
{
self.view.backgroundColor = .cyan //just makes the background colour cyan
}
}
I have also made a youtube video explaining this here

#IB Action function inside of viewdidload in Swift

Is it possible to have an #IB Action function inside of viewDidLoad() ?
The action is a simple one - a Stepper that increases other label.text values accordingly. However, the values that the stepper needs to work with depend on the return content of a url - which are only known after the viewDidLoad() of course.
So I think I can't have the IBaction way up on top before the viewDidLoad(), and the error I get if I try to do my IB action inside of the viewDidLoad() is:
"Only instance methods can be declared ‘IBAction' ”
EDIT
Let me clarify myself, sorry for the confusion. I know I need an outlet to get the UIStepper values from. I have that:
#IBOutlet weak var stepper: UIStepper!
I then have an action also connected to same UIStepper that will increase/decrease value of a label's text (new_total) accordingly:
#IBOutlet weak var new_total: UILabel!
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value))"
}
However, I want to start out with a value (todays_price) I'm getting back from a json request and use that as a starting point, to multiply it using the stepper and put the multiplied value into the label's text.
I have a struct in a separate file that defines my object so:
struct PassengerFromOtherBus {
var fname: String?
var lname: String?
var todays_price: Int?
init(json: NSDictionary) {
self.fname = json["fname"] as? String
self.lname = json["lname"] as? String
self.todays_price = json["todays_price"] as? Int
}
}
So later on in the view controller, inside of the viewDidLoad(), after connecting to the URL and then parsing it using NSJSONSerialization and a bunch of other code here (that I don't need to confuse you with) I finally have my value todays_price. So my question is, how do I get my action to use that value when it's only known inside of my viewDidLoad()? Xcode will not even let me connect the IBAction to anywhere inside the viewDidLoad function!
This is not done with an Action but with an Outlet. Connect the Stepper from IB as an Outlet to your ViewController. Then just set the values of the Stepper in ViewDidLoad.
I would never go directly from a UIStepper.value to UILabel.text.
Use an intermediary variable to store the value.
Do the same for the return from the JSON. By setting a didSet function on those variables you can update the UI when any of the values is updated.
class FirstViewController: UIViewController {
var todays_price: Int = 0 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
var stepperValue : Int = 1 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
#IBOutlet weak var myStepper: UIStepper!
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
//
let returnValueFromJson = 10
todays_price = returnValueFromJson
}
#IBAction func stepperUpdate(sender: AnyObject) {
stepperValue = Int(myStepper.value)
}
}
Just add a variable to the top of your view controller to hold the value from your json request. Then in viewDidLoad you update that variable, and then you can use it to set your label and inside the IBAction (that doesn't have to be inside viewDidLoad).
So you would do something like this:
class WhateverViewController: UIViewController {
var todays_price: Int!
override func viewDidLoad() {
super.viewDidLoad()
todays_price = // The value you got from json goes here
new_total.text = "\(todays_price)"
}
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value) * todays_price)"
}
}

Resources