How would I be able to increase/decrease the size of my UILabel by using UISlider which is in a different viewController?
I have a viewController1 that has the UILabel1 and I have viewController2 which has a UISlider. With the UISlider I have another label,UILabel2, just to see how big the text will be. I want UILabel1 to increase/decrease also instead of just one label to increase/decrease.
The code being used for UISLider is,
#IBOutlet weak var label: UILabel!
#IBOutlet weak var slider: UISlider!
#IBAction func sizeChanged(sender: UISlider) {
let senderValue = CGFloat(sender.value)
label.font = UIFont(name: label.font.fontName, size: senderValue)
}
This code with UILabel is for viewController2 and I want to change the size of another UILabel thats in viewController1.
This is viewController1:
import UIKit
class ViewController1: ViewController {
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var scrollView1: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView1.contentSize.height = 5000
scrollView1.contentSize.width = 375
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
This is viewController2:
#IBOutlet weak var label: UILabel!
#IBOutlet weak var slider: UISlider!
#IBAction func sizeChanged(sender: UISlider) {
let senderValue = CGFloat(sender.value)
label.font = UIFont(name: label.font.fontName, size: senderValue)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Any help would be great.
In computer programming, when something seems difficult or complicated, you should look for ways to break it down into smaller problems that are easier to solve. Yours is a fine example. You asked:
How would I be able to increase/decrease the size of my UILabel by using UISlider which is in a different viewController?
So, what are the steps that anyone would need to perform to make this happen? There are basically three steps here:
Get a slider's value.
Send a value from one view controller to another.
Use a value to set the size of a label.
How do I get a slider's value? This is straightforward. Set the slider's target to the view controller that manages it and its action to some action in the view controller. That method will be called when the slider changes. The action will look like this:
#IBAction func sliderValueChanged(sender: UISlider) {
let value = sender.value
// do something with the value
}
How do I send data from one view controller to another? There are lots of questions on SO that cover this. Of those, Passing Data Between View Controllers is perhaps the canonical one. There are lots of options, including:
Have one view controller call a method in the other. If the view controller with the slider has a reference to the one with the label, it only has to call some method when the slider changes to pass the new value to the other controller. Or, maybe it's the view controller with the label that has a reference to the one with the slider, which is pretty typical if the slider's controller is created by the label's controller. In that case, the label's controller can call a method to retrieve the value.
Broadcast the data to anyone who is listening using notifications. When the slider changes, it's action can post a notification with the new value. Any object, including the controller with the label, can listen for that notification and act on it.
Use a proper data model. The MVC (model-view-controller) paradigm is big in Cocoa, and if your app is anything beyond trivial it should have its own data model. That model may be a reasonable place to store the slider's latest setting, and the controller with the label can read it from there when its view appears.
Stash the value somewhere. Global variables are a short path to a badly design application, but their simplicity is appealing to beginners. A better choice may be the defaults system, which at least lets the value persist when the app quits.
So, lots of options there. Forget about the slider and the label and think about how the view controllers in your app should communicate with each other. Once you figure that out, the slider setting is just one more thing that they have to say to each other. The style you choose will tell you what to put in the action method above in place of the comment.
How do I set the size of a label? It's a little unclear what you mean by setting the size. Do you want to change the font size, or the width of the label, or the width and height? In any case, there are accessors for all the properties of a label that you might want to set, so check out the docs. When the label's view controller gets a new value via one of the methods above, it should update the appropriate property of the label. You typically connect the label to an IBOutlet property in the view controller to give the controller easy access to the label.
I think, you can look at NSNotificationCenter's functionality, especially at NSNotification's userInfo:
parameter. You can pass your slider's value to userInfo from first VC and then listen to this notification in second VC.
Great example of this method in Objective-C:
https://stackoverflow.com/a/7896761
Related
I understand it’s rather basic, but I’m only trying to get a grasp on basic functions.
I have produced some code by partially my own knowledge and partial bits from different guides.
I am not getting any errors, but the label is not displaying itself as “Text”. I believe it’s to do with the order/place my code is put.
Please help explain how I can fix this!
Please note as well:
I have just a single label called myLabel (named under the document section of my the identity inspector
It is has the text “Loaded” put into it already when I put it in.
I have no other code anywhere, only the default new project code.
I renamed the ViewController to ViewManager to avoid a class error.
First image: This is the image just so you know the location and other bits. I’ll attach the code too:
Second image: What I get, with no errors:
Third image: My main storyboard file:
And now it in code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
#IBAction func labelSet() {
myLabel.text = "Text"
}
}
Make sure that the IBAction is connected to Touch Up Inside in Interface Builder.
Change the signature of the IBAction to
#IBAction func labelSet(_ sender: UIButton) {
Your function func labelSet() isn't called anywhere. Neither in the Storyboard nor elsewhere.
You can call it in viewDidLoad() like this:
override func viewDidLoad() {
super.viewDidLoad()
labelSet()
}
Alternatively call it after the label has loaded.
#IBOutlet weak var myLabel: UILabel! {
didSet {
labelSet()
}
}
I have a little problem regarding the timing of an animation.
The sequence should be:
Button is pressed
Animation is displayed
Label is changed
In reality the sequence is:
Button is pressed
Label is changed
Animation is displayed
Here is the code of the main VC:
import Foundation
import UIKit
class MainVC: UIViewController {
var counter: Int = 0
#IBOutlet weak var counterLabel: UILabel!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var animationView: AnimationView!
#IBAction func nextButtonPressed(_ sender: UIButton) {
counter += 1
animationView.progress = 1
//this view draws a progress bar and animates it from 0 to 100% via CABasicAnimation.
counterLabel.text = "\(counter)"
animationView.progress = 0
//the progress bar is reset
}
override func viewDidLoad() {
super.viewDidLoad()
nextButton.setTitle("Next")
counterLabel.text = "\(counter)"
}
}
I've experimented with the dispatch queue, but I just can't get it to work right. Any ideas on how to solve this?
You don't show enough of your code for us to be able to help you specifically.
In general, you would set some object to be the delegate of your CAAnimation, and implement the method func animationDidStop(_ anim: CAAnimation, finished flag: Bool).
In that method you'd then change the label text (assuming finished == true.)
I don't really like the delegate pattern for animations. If you have multiple animations it can be painful to handle custom completion code for each one. What I've done is use the fact that you can attach objects to CAAnimation objects, and attach a closure to the animation. I then set the view controller to be the delegate, and in the view controller's animationDidStop(), I look for a closure attached to the animation, and execute it if there is one.
Edit:
See this article for a discussion on using key-value coding to attach objects to CAAnimation and CALayer objects:
Core Animation Key-value coding extensions
It's old, and written using Objective-C, but the concepts are the same in Swift, and very useful. For a more general discussion on key-value coding, which is updated for Swift, see this article: https://developer.apple.com/documentation/objectivec/nsobject/nskeyvaluecoding
(The two functions you need to know in Swift are setValue(_:forKeyPath:) and value(forKey:).)
When I want a tap response on my main View in my ViewController
A. I could create an IBOutlet as below
class ViewController: UIViewController {
#IBOutlet var tapGesture: UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
tapGesture.addTarget(self, action: #selector(tapped))
}
#objc private func tapped(_: UITapGestureRecognizer) {
print("Log is here")
}
}
Or
B. I could an IBAction on the TapGesture such as below
class ViewController: UIViewController {
#IBAction func tapGestureAction(_ sender: Any) {
print("Log is here")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Is there a preferred approach of one above the other? If not, which situation should we use A approach, and which we should use B approach?
Option B, i.e. just having the #IBAction outlet would be preferred when you already created your UITapGestureRecognizer in the storyboard, as this encapsulates as much logic as possible in the storyboard, reducing the overhead of reading unnecessary code and potential regressions if/when the code is refactored (but the storyboard remains unchanged).
You can still mark the #IBAction private (as it's effectively the same as using an #objc attribute). Also, if you need to access the gesture recognizer itself, you can have a regular #IBOutlet with a didSet to modify it, or change sender: Any to sender: UITapGestureRecognizer to access it in the action.
It is an interesting question, from my perspective this depends on how much from your application is in the storyboard or you want it explicitly written in the code.
My recommendation will be if you are doing something small and it should be done fast to use your storyboard. But if you have a big project with a big team then it will be better to have it in the code.
The other thing that can be a key factor for these approaches will be who is the owner of the reference and do you want to have some interactions of the gesture. For example, I have a gesture that should be enabled in specific cases and for others, it should be disabled. For this, you need to have a reference in the code.
What I'm trying to explain is that you should think for criteria like how and when you can use this gesture. And based on this to decide if you need less code or reference to the gesture or whatever you need
I would like the user to hit the button add 1. Which then will display in the middle of the green box. So the user hits a button and the number one is displayed in the middle of the green box. This is not a counter so its just one. Think of it as a score card. The green box is referenced as score display.
import UIKit
class ViewController: UIViewController {
#IBOutlet var scoreDisplay: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func add1(_ sender: Any) {
}}
If I understood correctly, you wish to hide and show some views depending upon some user behaviour.
As of Swift 3, please take a look at the isHidden property of any UIView. You must have some #IBOutlets for your buttons, not only #IBActions.
A possible solution for you (write this in your add1 function):
scoreDisplay.setTitle("1", for: .normal)
scoreDisplay.isHidden = false
Of course, do not forget to firstly set the scoreDisplay as hidden in the Interface Builder.
All of the searches I've done focus on passing data between view controllers. That's not really what I'm trying to do. I have a ViewController that has multiple Views in it. The ViewController has a slider which works fine:
var throttleSetting = Float()
#IBAction func changeThrottleSetting(sender: UISlider)
{
throttleSetting = sender.value
}
Then, in one of the Views contained in that same ViewController, I have a basic line that (for now) sets an initial value which is used later in the DrawRect portion of the code:
var RPMPointerAngle: CGFloat {
var angle: CGFloat = 2.0
return angle
}
What I want to do is have the slider's value from the ViewController be passed to the View contained in the ViewController to allow the drawRect to be dynamic.
Thanks for your help!
EDIT: Sorry, when I created this answer I was having ViewControllers in mind. A much easier way would be to create a method in SomeView and talk directly to it.
Example:
class MainViewController: UIViewController {
var view1: SomeView!
var view2: SomeView!
override func viewDidLoad() {
super.viewDidLoad()
// Create the views here
view1 = SomeView()
view2 = SomeView()
view.addSubview(view1)
view.addSubview(view2)
}
#IBAction func someAction(sender: UIButton) {
view1.changeString("blabla")
}
}
class SomeView: UIView {
var someString: String?
func changeString(someText: String) {
someString = someText
}
}
Delegate:
First you create a protocol:
protocol NameOfDelegate: class { // ": class" isn't mandatory, but it is when you want to set the delegate property to weak
func someFunction() // this function has to be implemented in your MainViewController so it can access the properties and other methods in there
}
In your Views you have to add:
class SomeView: UIView, NameOfDelegate {
// your code
func someFunction() {
// change your slider settings
}
}
And the last step, you'll have to add a property of the delegate, so you can "talk" to it. Personally I imagine this property to be a gate of some sort, between the two classes so they can talk to each other.
class MainViewController: UIViewController {
weak var delegate: NameOfDelegate?
#IBAction func button(sender: UIButton) {
if delegate != nil {
let someString = delegate.someFunction()
}
}
}
I used a button here just to show how you could use the delegate. Just replace it with your slider to change the properties of your Views
EDIT: One thing I forgot to mention is, you'll somehow need to assign SomeView as the delegate. But like I said, I don't know how you're creating the views etc so I can't help you with that.
In the MVC model views can't communicate directly with each other.
There is always a view controller who manages the views. The views are just like the controllers minions.
All communication goes via a view controller.
If you want to react to some view changing, you can setup an IBAction. In the method you can then change your other view to which you might have an IBOutlet.
So in your example you might have an IBAction for the slider changing it's value (as in your original question) from which you could set some public properties on the view you would like to change. If necessary you could also call setNeedsDisplay() on the target view to make it redraw itself.