I am new to iOS development, and I wonder how to do the following task:
I want the user to navigate through a multi-level menu. I have UITableViewController with two different kinds of cells.
When clicked on cell type number one I want to reload the UITableViewController with different data. (A click on the other cell does something else.)
Do I have to set up a different UITableViewController in the StoryBoard or can I reuse the existing one and segue from and to the same one? I don't know how many levels I will have in the end because the data comes from a remote source. I could have two different UITableViewControllers (because there is one different button), but I'd prefer to do it all in one (and try to hide and show the button when needed).
Anyway, I need to reuse the same class but change the data for each instance. How can I do that? I also need to restore the old state (last used data) of the controller when going back - or does this happen automatically - is it an "unwind" anyway?
Which methods should I override to delegate the new data?
I tried several tutorials but couldn't yet find the information needed.
Thanks in advance
Read up on Nav Controllers, I'm pretty sure this is what you'll use.
Add UITableViewDelegate to your ViewController press command and click on it for more information.
You can always just use a simple variable to keep track.
Declare a variable var step = 0
Then in your
override func prepareForSegue(){
step += 1 //and pass that variable to the next view and so one, incrementing by one each time.
}
Then in your
viewDidLoad(){
switch step{
case 0:
//add this data
case 1:
//add this data
case 2:
//add this data
default: break
}
Related
trying a few things out in Swift. I’m trying to get some things that seem muddled to me straightened out - mostly to do with how I deal with variables and referencing them in a project.
What I am trying to do is keep a variable (based on a struct) defined in ViewController accessed and updated from various other functions within an application.
So, a brief outline of the code I have is here. I actually wrote a smaller app to test my ideas out before applying them to something more complex.
I started in XCode with a Swift document based application for Mac OSX.
In ViewController.swift I have:
import Cocoa
class ViewController: NSViewController {
var myText = "Hello, some text"
#IBOutlet weak var textView1: NSTextField!
#IBAction func Button1(_ sender: Any) {
myText = "This is button 1 clicked"
myText = setText( thisText: &myText )
textView1.stringValue = myText
}
#IBAction func Button2(_ sender: Any) {
print("Button 2")
myText = "This is button 2 clicked"
textView1.stringValue = myText
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
textView1.stringValue = myText
}
override func viewDidAppear() {
let document = self.view.window?.windowController?.document as! Document
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
In SetText.swift, I have this:
import Foundation
func setText( thisText: inout String) -> String {
thisText = "Function"
return thisText
}
I liked the idea of sending variables to the Set Text function and then returning it, but thinking about it more makes me think that actually this could end up like a proverbial bowl of spaghetti with functions calling functions and who know what else. So I was thinking that something like this would probably make more sense:
import Foundation
func setText( thisText: inout String) {
let vc = ViewController()
// Read the variable from View Controller
var myTextHere = vc.myText
myTextHere = myTextHere + " More text"
// Set the variable in ViewController here
vc.myText = myTextHere
}
From my reading around on this subject, if I call ViewController(), it will create anew instance of the view (is that right, or am I misreading?). That’s already happened, so what I need is to reference the ViewController that called the function setText, or rather that owns that particular instance of code. As I’m thinking about a document based application, I’d obviously want to keep all instances of myText with each document’s ViewController.
My aim is to create something a bit more complex, using a variable based on a Struct to keep everything together. So:
myCard.image // holds a bitmap image
myCard.size // holds the size of the image
And so on. being able to access it in the form of ViewController().myCard to both read and write to is what I am thinking I need to do.
What I don’t want to do is use global variables.
Thanks.
I'm having a hard time seeing much correlation between the title of your question and the body of your question. In fact, I'm not even entirely sure that there's a question being asked. That said, I'll try to address the questions you appear to be asking (re-written how I think they're intended):
When you initialize a new view controller, does it create a new view?
Yes. There is a view property for every view controller, and it's not a shared component or a singleton or anything else like that. There is one main view for every view controller. It's almost certainly composed of dozens of other subviews, but there is one view that is the view for every view controller.
Is there a way to get metadata about the state of a view controller from outside, preferably in the form of a struct?
Absolutely. First, you'd need to define that Card struct. I'd recommend doing it in the same file as the view controller itself. You can define it outside of the view controller, or if you want stricter coupling and namespacing, you can do it inside the view controller. Just be aware that doing that latter would mean that the type name, when referenced outside the view controller, would be ViewController.Card rather than just Card.
Then you'd want to create either a computed property (var card: Card) or a method (func card() -> Card) on your view controller which builds and returns one of those based on the state. It sounds like you're already leaning toward the property approach.
Note: I would absolutely advise against having one that is a normal get/set property because then you'd have to constantly update it and modify it. The best thing to do is have a computed property which builds it on the fly. So when the property is called, it reaches into all your components to get the info you want (like image sizes, strings, etc) and then packages up and hands off the Card metadata struct. Doing it on-demand like this eliminates unnecessary complexity and consolidates the metadata logic into one place.
Some dangerous things in your code example:
I can't think of a good reason to implement viewDidAppear() but not call super.viewDidAppear(). Unless you have an enormously compelling reason to leave that out (I honestly can't think of a single one), do not do so.
I don't see any good reason for your first implementation of your setText(thisText:) method. The way you use it in your Button1(_ sender: Any) IBAction functionally does absolutely nothing. That method in general is screwy for several reasons: it's got an upper-case method name, sets the textView text by trying to assign to stringValue for some super strange reason, and does in three lines what could be done in one:
textView1.text = "This is button 1 clicked"
The second implementation of setText(thisText:) makes even less sense than the previous. The two biggest problems being 1) that you don't even use the thisText argument passed in, and 2) your method, which is called 'set text' is creating a whole new view controller every single time it gets called? That's a huge violation of "doing what it says on the tin." Methods should have a single responsibility and shouldn't do anything beyond that responsibility. I'd never in a million years look at a method called setText and think "I'll bet this initializes a view controller." Never.
I see this question has already been downvoted a bit (not by me), and I'd like to take a moment to coach you in using Stack Overflow: Ask clear, concise, specific questions about clear, concise, specific topics. As I said at the top of my answer, there doesn't appear to be a question anywhere in your post. I had to kind of make some up that I inferred from what you wrote.
Remember: coding isn't just wiggling your fingers while you think about an app. If you're doing the hard work of good engineering, you'll likely spend a ratio of about 10:1 (or more!) of staring at your screen to actually typing any code. Every time you write a line of code, you should be asking yourself, "Why am I writing this line of code? Is this necessary? Am I reinventing the wheel?)
Good luck!
I would like to remove the top left back button.
I tried to check "Full screen" option, but the arrow is still here.
I want to remove the back button because I have a button next to this button, and I don't want the user tap on it by mistake
Thx
According to the Apple Documentation the deprecated method that was in the previous answer (which used to solve this issue) has been replaced with:
reloadRootPageControllers(withNames names: [String],
contexts: [Any]?,
orientation: WKPageOrientation,
pageIndex: Int)
"Parameters
names
An array of NSString objects, each of which contains the identifier of an interface controller in your storyboard file. The order of the identifiers in the array defines the order of the corresponding interface controllers in the page-based interface.
contexts
An array of objects of type id. Use this parameter to pass context objects to each of the interface controllers loaded into the page-based interface. The first object in the array is passed to the first interface controller, the second object is passed to the second interface controller, and so on.
orientation
The scrolling orientation for the page-based interface. For a list of valid values, see WKPageOrientation."
And pageIndex should be pretty simple to figure out. If you only have one element in [names] it will be 0. Otherwise you should select the index of the page you want loaded from [names]
Try:
WKInterfaceController.reloadRootControllersWithNames(["myInterfaceController"], contexts: [])
This removes it by making the controller the root controller. You may have to reload the controller after calling this.
I'm trying to modifyt this repository which I forked https://github.com/vinnytwice/BicycleSpeed
but without the use of the tableviews because i'm interested in display the data like speed cadence and distance inside a more graphically designed InfoSpeedoViewController.
I don't manage tableviews controller yet so I can't understand how and where to modify the project.
Can anybody help to point me in the right direction?
my version will have just two ViewControllers :
MainViewController with a ScanButton and SpeedoViewController
So far I deleted the InfoTableViewController.swift, that is just drawing the tableview. but I'm stuck in modifying MainViewController according to my needs. I don't understand the override function prepare(for segue ) part of the code. it passes data to the InfoTableViewController with the first if statement so I don't need it, but with the second if statement it starts the scan function bluetoothManager.startScan() and passes data to ScanViewcontroller via it's UINAvigationController and than to the tableView in the ScanViewController. Am I right?
If so, could I just call the scan function and pass data to my SpeedoViewController instead?
#IBAction func scanButton(_ sender: UIBarButtonItem) {
bluetoothManager.startscan()
performSegue(withIdentifier identifier: MainToSpeedoSegue, sender: self)
}
I don't really understand the relationship with scanViewController I guess. does data end to the infoTableView anyway?
thank
Finally found the solution. it was to change the tableViewController for a normal ViewController having labels to display values. Change the references from the tableViewController to the ViewController, and link the returned values to the labels inside the function that was receiving the continuously updated values.
Pretty easy once decoded the data flow.
I have three elements in my UICollectionViewCell:
Two labels with name and price, and a quantity button.
I would like to add the name and the quantity with "didSelectItemAtIndexPath" to a specific collection view in the previous view controller and the price in another view controller. I may achieve this by using a segue to pass data to any view controller of my choice.
Also, at the same time, I want to keep track of the selections I make adding them to a table view below the collection view.
My guess is to create empty arrays for each item.. one for name, price and quantity.
I may be wrong.. and I tried to append my current selection and I know I am missing something.
To pass information to a previous viewController use a delegate protocol. Suppose a is your first viewController, and b is your second viewController.
You can create a delegate protocol in b (here you specify a func you want to execute inside of a)
Set a as the delegate for b
write a function yourDelegateFunction in a , which your delegate protocol will call to update the viewController with your first collection.
in didSelectItemAtIndexPath call delegate.yourDelegateFunction
Here's a video with an easy guide for how this works:
https://www.youtube.com/watch?v=3BcBu30thIA
If you provide a sample of your code, and highlight where you experienced issues, I can provide a more precise solution.
I'm trying to pass int variable to UITableView through UINavigationController (I'm using xcode 4.3) So I created 2 classes (PartsTableViewController that is "UITableViewController" and PartsNavController that is "UINavigationController"), I want to pass the variable from my current class to PartsTableViewController and then open that table with its Navigation controller that contains the title bar , so I wrote in my current class the following code:
PartsNavController *partsNav = [self.storyboard instantiateViewControllerWithIdentifier:#"partsNav"];
partsNav.groupId = myGroupp.bg_id;
[self presentModalViewController:partsNav animated:YES];
and in the PartsNavController class I wrote in viewDidLoad:
PartsTableViewController *parts = [self.storyboard instantiateViewControllerWithIdentifier:#"Parts"];
parts.groupId = groupId;
[parts.tableView reloadData];
and in PartsTableViewController I wrote in viewDidLoad:
NSLog(#"This is group: %d", groupId);
but when run, it generates the output 2 times,
This is group:1
This is group:0
first time is the value that I sent and the second time it outs 0 , I just want the value that I sent, not 0
how can I prevent this and get just the value that I sent ????
I want to pass from (MaktabatyTableViewController) to (PartsTableViewController) without using segue
The better way to do what you want is to push second TableViewController in existing UINavigationController. The easiest way to do that is to create that NavContr in StoryBoard and than to TableViews and connect it's cell with leading view controller with segue. And than use method below:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UIViewController *destViewController = segue.destinationViewController;
destViewController.integerValue = value;
}
I want to pass from (MaktabatyTableViewController) to
(PartsTableViewController) without using segue
There are (at least) two strategies you can choose from:
Direct communication: People often ask how to communicate between two objects, and it almost always boils down one of the objects having a reference to the other (and sometimes vice versa). To send a message to an object, you need a pointer to that object; if you've got the pointer, there's no mystery about how to communicate. Thinking about it in those terms helps you think about the issue a little differently: instead of the immediate "how do I send a message to that object?" you can instead focus on the relationship between the two objects. How was each one created? Is one of the objects the parent of the other? Is there some common parent object that can provide a pointer? How should the objects be related, if at all?
Indirect communication: Sometimes instead of having two objects communicate directly, it's more appropriate to route the communication through some intermediate object. For example, your MaktabatyTableViewController might send a message to its delegate, and the delegate could then pass the information on to PartsTableViewController. A much more general solution is to use notifications: MaktabatyTableViewController could post a notification that PartsTableViewController listens for. The intermediate object in this case is the notification center. Either way, the advantage that you get with indirect communication is that neither object has to know about the other. That reduces coupling between the two classes and makes them both more flexible and more reusable.
From what I can see in your question, I'd suggest using notifications.