How to dynamically change view in Swift (e.g. for a settings page)? - swift

I found a good settings page tutorial at https://www.simpleswiftguide.com/how-to-use-userdefaults-in-swiftui/ and implemented it into my app. However, the settings page is completely static and can't be changed based on the user's choices. Often a program needs to have constraints where the allowed choices for field B might change based on the user's choice of field A. How do I add an on-click listener to the settings which can call the UIViewController to somehow change the structure of the view after it has already loaded? I am using UIHostingController to wrap the SwiftUI View which is declared as a struct instead of a class. If I try to access the view via self.view I don't see any relevant functions that could give me access into the inner workings or fields of the view.

It doesn't look like there's an easy way to do this in Swift like there is in Android. I'll resort to using a sequence of UIAlertController each with multiple choices. Unlike the View struct with "var body" protocol, the alerts can be built programmatically depending on what the user has already set.

Some if else in the View
if $userSettings.username.wrappedValue == "username"{
SomeView()
}else{
SomeOtherView()
}

Related

Is it possible it use a link in a SwiftUI AttributedString to navigate to another view in the same app?

I know with AttributedString, I can link to an external website using something like:
Text("To learn more about AttributedString visit [this page](https://developer.apple.com/documentation/foundation/attributedstring/)")
Can I use this to go from ViewA to ViewB within the same app, kind of like this example in Twitter?
You can store an OpenURLAction in the environment to override how subviews like Text and Link open URLs.
This example is from the documentation:
Text("Visit [Example Company](https://www.example.com) for details.")
.environment(\.openURL, OpenURLAction { url in
handleURL(url) // Define this method to take appropriate action.
return .handled
})
You may want to apply the environment modifier at a high level of your view hierarchy (near your root view) rather than directly on each Text view.

Xcode 4.5 Storyboard 'Exit'

I have just installed Xcode 4.5 for iOS6 support, and I have seen a new icon called 'Exit' in my Storyboard, listed under my view controllers along with 'First Responder' etc. A little green icon labeled 'Exit'.
I can find anything about it, nor work out how it can be used. How does it work?
This is called an "Unwind Segue". Unfortunately there's no documentation for this so far except a brief mention on XCode 4.5 new features list that states:
Unwind segues can allow transitioning to existing instances of scenes
in a storyboard
The good news is that there is a session from WWDC 2012 explaining those creatures (among other things).
You can just login to Apple's iOS Dev Center with your developer account details and then go to the WWDC 2012 videos page and watch "Adopting Storyboard in your App" (it's fifth from the top) The discussion of unwind segues starts at time 37:20.
Update:
Here is some more info on the subject from Apple's documentation
A placeholder object named Exit for unwinding seques. By default, when
a user dismisses a child scene, the view controller for that scene
unwinds (or returns) to the parent scene—that is the scene that
originally transitioned to the child scene. However, the Exit object
enables a view controller to unwind to an arbitrary scene.
(From iOS6 docset > General > Getting Started)
And here is a nice example of how you can implement one
Another Update:
Here is a technical note from Apple regarding this topic.
Imagine you have a sequence of views in your storyboard:
A -> ... -> Z
You want to have a button on view Z which allows the user to go all the way back to A.
So what you need to do is give the view that you want to back all the way out to, in this case, A, an instance method which is marked as a IBAction and takes in a single parameter of type UIStoryboardSegue *. The name of the method and variable don't matter. What you do within the implementation doesn't matter, either. Here's an example:
Obj-C:
In A's Interface (not Z's):
- (IBAction)cancelSignup:(UIStoryboardSegue *)unwindSegue;
In A's Implementation (not Z's):
- (IBAction)cancelSignup:(UIStoryboardSegue *)unwindSegue {
// Only "implemented" to satisfy a respondsToSelector: search.
// You can actually implement more stuff here, if you want, IE, if
// you need to reach out to a server to mention that this screen was
// returned to from a later screen.
}
Swift:
In A's source (not Z's):
#IBAction func cancelSignup(unwindSegue: UIStoryboardSegue) {
// Only "implemented" to satisfy a respondsToSelector: search.
// You can actually implement more stuff here, if you want, IE, if
// you need to reach out to a server to mention that this screen was
// returned to from a later screen.
}
Now, within your storyboard, control drag from an element on Z (IE, a cancel button) to Z's Exit. It'll scan through all of the views higher up in the view hierarchy which have an IBAction that accepts only a single UIStoryboardSegue * as an action and list them for you to pick from.
Hopefully this was more straight forward and helpful than the existing answers. I found that this link was particularly useful, so if there's a detail you're still fuzzy on after reading my answer, maybe this can help you (I tried to just condense all the useful info from this long article into a short answer):
http://www.freelancemadscience.com/fmslabs_blog/2012/9/24/advanced-storyboard-techniques.html
See also Cannot Connect Storyboard Unwind Segue which clarifies the requirements to bring the Exit icon to life. You must have, higher up in the view controller hierarchy, a method that is:
Marked as IBAction
Takes one parameter that is a UIStoryboardSegue*
If both those conditions are met, the Exit icon will see it and will permit you connect through to it by control-dragging from a button in the same view controller.
I have also now posted the world's simplest example here:
https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch19p638presentedViewControllerStoryboard (fixed 12 July 2013)
This shows how trivially easy it now is to segue to and back from a presented view controller, as opposed to all the work you had to do previously in order to hand info back and forth (the stuff in the template with a delegate and a protocol, all of which can now be deleted).
Just adding a slight subtlety to the requirements definition that might help. This is based on experimenting in Xcode 4.6. I found that it is specifically and only the declaring(!) of the method that enables the desired control-drag response from Xcode. Here's what I found to be the full requirements:
Marked as IBAction
Takes one parameter that is a UIStoryboardSegue*
You must have an action declared (but not necessarily implemented [meaning a method in the .M implementation section]).
It can be in any class's interface declaration, even the interface section of a .M, except the appdelegate class. (I did not see any dependency on its position in the controller hierarchy. You can add any old file and the system seems to aggregate all the methods that have the UIStoryboardSegue parameter and display them on the Exit icon's menu.) Note that the control-drag menu will even show you your method if the method is in the class of the scene you are manipulating in the storyboard editor, but it will appear without a colon and does not seem to trigger any action at runtime.
Example:
-(IBAction)anymethodname:(UIStoryboardSegue *)myvariable;

How to get/set the rootViewController?

Now,I gonna Develop an App ,which wants to switch from many different Views irregularly,also the views need to load large resources,AKA,it's hard to manage memory.Are there any good solustion?
PS:I created a ViewController as RootViewController,and When a button was Touch,run the code as
"ViewController=newController"
.The problem came,The new View loaded wrong way,it rotate so that couldn't show in a correct way.
I google for the solution,some one said ,I should replace the rootViewController,just like that,
[UIApplication sharedApplication].delegate.window.rootViewController=newController;
But I can't get/set the rootViewController in other class though it's a singleton.
Why not having a class that handles all the view switches ?
This article describes an architecture that might be helpfull: http://www.mikeziray.com/2010/01/27/handling-your-initial-view-controllers-for-iphone/comment-page-1/#comment-607

access UISwitch setting from a different view?

k, I'm new to this so apologies all around, generally.
I'm trying to access the UISwitch value (on or off) from a different view and class and can't make it work.
It's a simple 2 view app. Main view and the second is a preference menu.
trying to write an if/else method to play sound when the switch (on the other view) is on and not when its off.
I cant seem to make it work. Any thoughts or some syntax examples would really help me out.
Thanks.
As Matt Wilding said "it's not good form to access UI components of one view controller from another...".
Instead of accessing the view object, when the switch state is changed by the user you save the status into NSUserDefaults as preference value. Whenever you want, you can access the switch status value through the preferences.
I'm going to take what I think you're trying to accomplish here and suggest an alternative approach. You want to have a preference in your app (assumed from "preferences menu") that allows the user to set something like whether or not you app plays background music. (May not be exact, this is just for clarification).
Typically, in a well designed app, the flow is driven by the data, with the UI reflecting the state of the data model and the controllers coordinating the two layers. What you are suggesting is to have your application play music based on the state of the UI, which is not backed by any data model. This cuts out the model level, and as you noticed, can lead to awkward attempts at communicating between the UI of different controllers for information.
Things like application preferences are typically stored in a nifty .plist file that is managed through the NSUserDefaults class. This would be a great place for the data level tracking of your preference. In this situation, the UISwitch would represent the state of the flag in the settings file, and changing the value of the switch would change the value in the file. Anywhere else in your application that you need to know if the play-sound-flag is set, you reference the data model info instead of the UI. This decouples the view controllers from each other, which is a good thing.
For this purpose add selector for swith and make NSInteger property in app delegate.Like the followed
[
yourSwitch addTarget:self action:#selector(switched:) forControlEvents:UIControlEventValueChanged];
-(IBAction) switched: (id)sender
{
int state=0;
if(yourSwitch.on)
state=1;
else
state=0;
objAppDelegate.switchState=state;
}
then you need to access this appDelegate property in second view where you are playing sound
then according to this value you can do what you want and for making object of appDelegate class you need this line
YourAppDelegateClass *objAppDelegate=(YourAppDelegateClass *)[[UIApplication sharedApplication] delegate];
ok if you have any other doubt then you can ask.

Objective C Callbacks and Notifications

I'm new to Objective-C and not a full time programmer. I'm beginning to understand the Model-View-Controller design pattern for differentiating the UI from the model. So the user takes an action and the view controller sends a message to the delegate (model). But I'm not sure what the best way to send actions from the delegate back to the view controller.
For example, the user pushes a button, the VC messages the Delegate. That part I understand. Then the delegate takes action, and following that the delegate wants to update the VC (e.g., update a label).
So what I missed (or have forgotten) is how this gets done, while maintaining separation between the UI and the model. I suppose I can use the notification center. Or I think I can just have the view controller pass a callback to the delegate. Or maybe there's another choice I don't know of. Can someone give me a recommendation, please?
I think you're slightly misunderstanding the MVC paradigm. Models should never be delegates of views, since models should have no dependencies or knowledge of any view classes. Typically, a view sends a message to its delegate or target (if you're using target/action), which is usually a controller (often a subclass of UIViewController on iOS). The controller then accesses data from the model and can update any views that need updating. I'd recommend reading the MVC fundamentals guide for a more complete explanation.
Basically you're right, you could do all the notification-related things yourself (i.e. with NotificationCenter) but since we're talking about UI-Stuff here I would greatly recommend you to use IBAction-Methods and IBOutlet-Properties in your code which you can easily connect to UI-Elements respectively their Callbacks in Interface Builder.
A very basic introduction to this topic can be found here:
iPhone SDK Interface Builder basic training
i hope that it is not too basic tough, and that I could lead you on the right track.
First of all delegate is NOT a Model.
Model is something passive that only holds the data (DB, plist, array, dictionary etc.).
While delegate is some set of functions that exist in order to react to some events.
Delegate is more likely to be a view controller in your case.
The view controller should react to user's action.
If the button tap should display some data from your model in some label then view controller should do all the work (receive user's action, take the necessary data from the model and display it on the view...).