UIActivityIndicatorView Swift stopanimation - ios

I have a swift project and hav3 an Activity Indicator declared in my ViewController. When I click a UIButton, the indicator starts. However, I can not access this indicator in my other class to stop it when the code is completed. Any help would be appreciated.
#IBOutlet var spinner: UIActivityIndicatorView! // <--- want to access in another class
#IBAction func sendClick(sender: UIButton) {
spinner.startAnimating()
var msg = Message()
msg.send()
}

Take a look at your scopes and access control!
If the two classes are not within the same module, you need to declare the object as public. If the object is even declared to be private, you can't access it from another file's class.
For more information about access control read this article from Apple's swift blog: https://developer.apple.com/swift/blog/?id=5
or here in the documentation:
https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html

Related

Where should I drag and drop "label"?

Now I'm learning swift using Xcode, but I don't know where to drag and drop and why drag and drop "label" to somewhere
should I drop no.1? or no.2? or no.3? and why I should drop there?
If you drop it to 1, Xcode automatically generate an #IBOutlet for you. If you drop it to 2 or 3, then #IBAction. There's no other reason for that except that Xcode tries to be smart and help you organize code more nicely: properties with properties in the top of the class, methods – in the methods area. And you can also move declarations to another place later: except for code style matters, it doesn't matter, where exactly within your class you put declarations.
as you are trying to take an IBOutlet for a label , its nice to keep it on top of the class (where you mention 1).
normally Xcode gives suggestion you , if you drug on top side it will be #IBOutlet
or you on bottom like 2 or 3 it will be #IBAction like you already took a button action in your code .
Your Solution On Code:
import UIKit
class CodePresentViewController: UIViewController {
#IBOutlet weak var YourLabel: UILabel! // insert #IBOutlet type property here
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func tapBackButton(_ sender: UIButton) {
//Yout Button COde
}
}
If you want to own the label in your code, and then make some configuration to the label by code, use "1". It will give you a #IBOutlet label object in your code.
If you want to set the label's action, use "2" or "3".

Human readable tag/id for a View in iOS

Let's say I have created a UI with some views using an interface builder (not in code, so not programmatically).
How can I assign human readable ids / tags to these views, so that I could reference them in code?
I know that I can assign an integer tag to a view using attribute inspector and then make a dictionary (or enum) to store the mapping of tags to the views. However, this is an error-prone method which also scales really badly (imaging assigning integer tags to hundred of views in a complex app...).
Is there a better solution for this problem? Is there a way to directly assign a human readable tag / id to a view, like "resumeButton"?
UPDATE:
Here is an example scenario of what I want to achieve:
UI with five different buttons; the buttons have image and no title
all five buttons are connected to the same IBAction in code
in IBAction I have a switch statement, so that depending on which button is clicked, different versions of code are executed
UPDATE 2:
SOLUTION
I ended up implementing a simple custom view:
#IBDesignable
class CustomButton: UIButton {
#IBInspectable var stringTag: String = defaultID
}
This way I can see an additional property stringTag in Interface Builder and can simply add a value to it directly in Interface Builder.
You could create an extension property on UIView to store an identifier string. If you made that extension property IBInspectable, you could set and view it from the storyboard directly.
More detail on setting up such a property in this answer:
https://stackoverflow.com/a/37166043/1830999
The built-in tag property for UIView is just an integer, so it isn't very descriptive for humans who read it.
From what you're describing sounds to me that you are looking for an: Outlets
You are right about tags. Every connection with Interface Builder should be handled using IBAction and IBOutlet.
Since you say that every button has a different action, the simplest solution is to create a separate IBAction for each of them:
#IBAction private func onResumeButtonTapped() {
...
}
#IBAction private func onPauseButtonTapped() {
...
}
If you, for some reason, want to keep them connected to one function, you can use outlets:
#IBOutlet private var resumeButton: UIButton!
#IBOutlet private var pauseButton: UIButton!
#IBAction private func onButtonPressed(_ sender: UIButton) {
switch sender {
case resumeButton:
...
case pauseButton:
...
default:
break
}
}

Changing a property of an object from GameViewController.swift in GameScene.swift

I'm new to Swift, so sorry if this seems like an obvious question.
I'm trying to write a game in Swift, and I have designed a label and a play button in the storyboard.
I've added references to these objects in the GameViewController.swift file as seen here:
// MARK: Objects
#IBOutlet var gameLabel: UILabel!
#IBOutlet var playButton: UIImageView!
// MARK: Actions
#IBAction func playButtonTapped(sender: UITapGestureRecognizer) {
GameScene().gameStart()
}
This belongs to the GameViewController class: class GameViewController: UIViewController.
However, the main game itself takes place in GameScene.swift. This involves me having to hide the label and image as soon as the game starts. I'm unsure how to set the properties for these objects to hide, since if I did the following in GameScene.swift:
GameViewController().playButton.hidden = true
...the game crashes with a fatal error. The error says: fatal error: unexpectedly found nil while unwrapping an Optional value.
Does anyone have a suggestion for this problem? Any help is much appreciated. Sorry if the information was difficult to understand.
Cheers.
You are creating a new instance of 'GameViewController(), but you want you want use your already existent one. If you only want to create oneGameViewController`, which is probably the case, you could use a static instance variable.
In GameViewController, put:
static var instance: GameViewController!
In it's viewDidLoad(), put:
GameViewController.instance = self
Finally, in your GameScene, switch GameViewController() with GameViewController.instance.

iOS using xib + global methods for overlay UIView (Swift)

I'm writing an app that should present overlays in specific situations, like for example the lack of location services enabled for the app.
Overlay is a UIView with a UIImageView (background) a UILabel (title) and a UIButton calling a specific action. I want to use Interface Builder to set up the overlay UI but I would like to recall the overlay and show it on different UIViewControllers, depending on when the lack of location services is detected.
I have set up a custom class (subclass of UIView) to link a xib file. Code below:
class LaunchCustomScreen: UIView
{
#IBOutlet var title: UILabel!
#IBOutlet var enableLocationButton: UIButton!
#IBOutlet var waitingIndicator: UIActivityIndicatorView!
#IBOutlet var bckgroundImage: UIImageView!
func setupDefault()
{
title.text = "Location Services Required"
enableLocationButton.setTitle("Enable Location Services", forState: UIControlState.Normal)
enableLocationButton.addTarget(self,
action: "promptUserForLocation",
forControlEvents: UIControlEvents.TouchUpInside)
hideLocButton()
}
func hideLocButton()
{
enableLocationButton.hidden = true
}
func showLocButton()
{
enableLocationButton.hidden = false
}
}
Then I have created the xib file which is of Class LaunchCustomScreen and I linked the IBOutlets to all the objects in it (UILabels, UIBUtton, UIImageView)
Then I have set some global functions to be called from any other UIViewController in order to show/hide the overlay on the specific view controller and configure it with UIButton hidden or visible (it will be hidden with a waiting indicator when user location is still loading). Below related code:
func setupLaunchDefault(vc: UIViewController) -> LaunchCustomScreen
{
for aSubview in vc.view.subviews
{
if aSubview.isKindOfClass(LaunchCustomScreen)
{
NSLog("Found already a launch screen. Removing")
aSubview.removeFromSuperview()
}
}
var screen: LaunchCustomScreen = LaunchCustomScreen()
screen.setupDefault()
return screen
}
func showLaunchAskLocation(vc:UIViewController)
{
var screen = setupLaunchDefault(vc)
screen.bounds = vc.view.bounds
screen.showLocButton()
vc.view.addSubview(screen)
}
Now I'm trying if the solution works and it crashes on the setupLaunchDefault function. Reason is that even if an instance of LaunchCustomSCreen is created, the variables (title, enableLocationButton) are still nil. I though they should be non-nil thanks to the IBOutlet to the xib... what am I missing?
Thank you in advance for your help!
I have set up a custom class (subclass of UIView) to link a xib file
No, you haven't. No such "link" is possible.
what am I missing?
You're not missing anything, because you've already figured it out!
Merely creating a LaunchCustomScreen instance out of thin air (i.e. by saying LaunchCustomScreen(), as you are doing) merely creates an instance of this class. It has nothing whatever to do with the .xib (nib) file! There is no magic "link" whatever between the class and the nib! Thus, nothing happens that would cause these properties to get any value. They are, as you have rightly explained, nil.
You have designed and configured one special particular instance of LaunchCustomScreen in the nib. That is the instance whose outlets are hooked up, within the same nib. So if you want an instance of LaunchCustomScreen with hooked-up outlets, you must load the nib! Loading the nib is exactly equivalent to making an instance of what's in the nib - it is a form of instantiation. And here, it's the form of instantiation you want, because this instance is the instance you want.
So, the answer is: do not say LaunchCustomScreen() to get your LaunchCustomScreen instance (screen). Instead, load the nib to get your LaunchCustomScreen instance - and all will be well.
So, let's say your .xib file is called LaunchCustomScreen.xib. You would say:
let arr = NSBundle.mainBundle().loadNibNamed("LaunchCustomScreen", owner: nil, options: nil)
let screen = arr[0] as UIView
The first result, arr, is an array of top-level objects instantiated from the nib. The first of those objects (probably the only member of the array) is the view you are after! So you cast it to a UIView and you are ready to stick it into your interface. Since the view comes from the nib, its outlets are set, which is what you're after. You can do this as many times as you need to, to get as many "copies" of this view as you like.

Pass data when dismiss modal viewController in swift

I'm trying to pass data from the modal ViewController to his source ViewController. I think I have to use delegation but it doesn't work.
protocol communicationControllerCamera{
func backFromCamera()
}
class Camera: UIViewController{
var delegate: communicationControllerCamera
init(){
self.delegate.backFromCamera()
}
}
class SceneBuilder: UIViewController, communicationControllerCamera{
func backFromCamera(){ // Never called
println("YEAHH")
}
}
The backFromCamera method it's not called. What did I do wrong?
You didn't set a delegate so it was empty when you tried to call backFromCamera().
Here's a simple working example you can test out. Notice the use of the optional type (?) for the delegate.
// Camera class
protocol communicationControllerCamera {
func backFromCamera()
}
class Camera: UIViewController {
var delegate: communicationControllerCamera? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.delegate?.backFromCamera()
}
}
// SceneBuilder class
class SceneBuilder: UIViewController, communicationControllerCamera {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
var myCamera = Camera()
myCamera.delegate = self
self.presentModalViewController(myCamera, animated: true)
}
func backFromCamera() {
println("Back from camera")
}
}
You can find all the information you need in Apple's Swift documentation.
Obviously the chosen answer is correct, but it didn't help me. I did successfully implement protocols though, so I wanted to provide my own explanation in case anyone is struggling with grasping the concept, like I was.
Protocol Code Is Written in Three Places:
Two ViewController Classes
The Protocol itself (code written outside of VC classes)
When I write my protocols, I put them in my "ToolBox" document and I still write comments to remind myself which VCs are doing what. Two examples:
So there is always:
The protocol code (shown above)
Code in a VC which initiates the action
Code in a VC which is delegated to carry out the action
1. The protocol code
See the image above for a reference. Essentially, the protocol code is just where you give the protocol a name and declare what functions you want to remotely call/delegate to. Name the protocol. Declare the names of the functions that can be called upon and declare their parameter types such as string, etc.
2. Code in a VC which initiates the action
This is the code that initiates the protocol. In this example, this is code from a table cell, which needs to delegate some work back to the main table VC. The first screenshot shows the creation of the delegate variable and the second screenshot is the actual use of that variable.
So the below code are table-cell buttons. They all need to trigger code outside of the cell VC, so they all trigger functions using the protocol I declared above.
3. Code in a VC which is delegated to carry out the action
Now the protocol is being called, but which VC answers the call? To answer that question, choose the VC and add the protocol name to the class declaration:
Lastly, you need the actual meat of the whole thing. Not the trigger, not the protocol itself, not the class declaration... but the actual function you want to call:
Hope This Helps
I don't know why protocols just wouldn't sink through my thick skull but they wouldn't. I hope this helps others like me!

Resources