Xcode ios Background image on multiple views - ios

I recently found how to program a switch and how to change the background from 1 view.
My question is how to change the background of multiple views (UIImage) when changing the value of the switch.
For example: It's a preference page and when I switch the background of the preferences page changes so the switch works.
Now i want the background (UIImage) of my first view named "viewcontroller" to change to the same background as the background of my preferences page.

in my opinion, there may be a lot of different ways to achieve this. What i would do is save the image name string into [NSUserDefaults standardUserDefaults].
Such as when the switch value changes, you call
[[NSUserDefaults standardUserDefaults] setObject:#"BackgroundImageName" forKey:#"BackgroundImageOne.jpg"];
[[NSUserDefaults standardUserDefaults]synchronize];
and when you set up the background, you could do
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
UIImage *bgImage;
if ([userDefault objectForKey:#"BackgroundImageName"])
bgImage = [UIImage imageNamed:[userDefault objectForKey:#"BackgroundImageName"]];
else
bgImage = [UIImage imageNamed:#"DEFAULT_NAME"];
//... Set the bgImage to your background image view ...//
Maybe you have to do some changes based on the actual usage. But hope this give you some hints.

One of possible solutions would be to create a public property of application delegate holding your background image.
#property (strong) UIImage *globalBackground;
In your - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions you should initialize it either to some default image or last saved image.
self.globalBackground = [UIImage imageNamed:#"defaultBackground"];
Let's say your appDelegate header file is MyAppDelegate.h. You should import it in all the classes that use this default background image mechanism. Or simply add it to your Prefix.pch file.
In your settings view controller you would then set:
//your background setting handler
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.globalBackground = ... //however you get the selected image
//and here you set the background image of settings view controller
Then in each of the view controllets viewWillAppear: you set its background in similar manner:
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
//then you use appDelegate.globalBackground the same way you are doing it now
If you have a lot of view controllers you might want to consider creating a subclass of UIViewController that implements this mechanism (possibly calling it UIViewControllerWithBackground) and make all your view controllers subclasses of this class.
Note A: one would (instead of appDelegate's property) typically use a singleton class holding all the settings. It might be considered as a bad (quick & dirty) practice. More...
Note B: for saving the name of last selected background you could use NSUserDefaults. This is out of the scope of this question and there is a lot of sample code available out there...

Is it possible to pass the image, which I saved in Xcode, to a next viewcontroller? Or give it a number and pass that number to a next viewcontroller? For example, when switchone is on, NSNumber = 1, pass it to the next view, and then if (NSNumber == 1) than display "backgroundone.jpg"???

Related

iOS Toggles don't retain their state between view controllers

I have several toggles assigned to sounds. When switched on, a short clip plays for 30 seconds or so. Then when switching between viewcontrollers inside the app (eg, to the menu page) when i go back to the toggles they are all off! I would like them to retain their value after being switched on until the user decides to turn them off. This is not an issue until switching between pages inside the app.
Thanks for your help.
-(void)kickSwitchChanged:(UISwitch*)sw
{
AppDelegate* app = [[UIApplication sharedApplication] delegate];
[app setKickStep:sw.tag state:sw.isOn];
}
-(void)snareSwitchChanged:(UISwitch*)sw
{
AppDelegate* app = [[UIApplication sharedApplication] delegate];
[app setSnareStep:sw.tag state:sw.isOn];
}
What you have sounds like a good use case for NSUserDefaults.
NSUserDefaults allows you to store data that will be persisted all the way through terminating and relaunching your app. It is meant to be used for "lighter-weight" data, like if a user wants a switch on or off.
Here is an example of saving a boolean default:
let kMySwitchDefaultKey = "isMySwitchOn"
NSUserDefaults.standardUserDefaults().setBool(mySwitch.on, forKey: kMySwitchDefaultKey)
Here is an example of retrieving the same boolean default:
let wasMySwitchOn = NSUserDefaults.standardUserDefaults().boolForKey(kMySwitchDefaultKey)
Typically you would set the default when the user changes the switch value, and you would get the default somewhere like viewWillAppear and use the saved value to update the switch state to be what it previously was set to.
Source: https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/index.html
Edit
I didn't see your Obj-C tag. Here is the same setting and getting code in Obj-C.
Setting
NSString* const kMySwitchDefaultKey = #"isMySwitchOn";
[[NSUserDefaults standardUserDefaults] setBool:mySwitch.on forKey:kMySwitchDefaultKey];
Getting
BOOL wasMySwitchOn = [[NSUserDefaults standardUserDefaults] boolForKey:kMySwitchDefaultKey];

When to unregister a delegate of a UIView or UILabel

I got a customized UILabel which register itself to a communication instance during creation. This part works perfect, the UILabel will . But when I remove the UILabel will update itself after called by the communication instance. I have of course remove the listener delegate too. But as ViewDidUnload is not call anymore, I do not know where.
Here the code sample:
#implementation MyValueLabel
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate addBalanceListener:self];
}
return self;
}
-(void)communicationUpdate
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
}
// This is the missing Method
-(void) DestructionOfTheLabelWhichIDidNotFound
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate removeBalanceListener:self];
}
#end
Technically you seem to be looking for the -dealloc method which is called on an object when it is to be deallocated. However I do not believe that is an adequate solution.
The example provided here seems to be doing a number of unexpected things which I believe will result in code which is surprising to other developers, requires fighting against common patterns in the UIKit framework, and be difficult to maintain.
Normally iOS view controllers are responsible for mediating or coordinating how data reaches view objects. A controller supplies a view with either the data it should currently display or provides it with a source of that data. This allows a single view class to be used in many different locations and to display data of the same type regardless of its source. Instead here we have a view which reaches out to obtain it's own data. There's no way for a controller to determine when an instance of this view class should update or what data it should display. For example the controller cannot decide to pause updates, or cause other view elements to update at the same time.
In addition this view makes several assumptions about the source of it's data and how that source may be obtained. The view assumes that the current application's app delegate is specifically an AppDelegate class so it will not work if you want to use this view in another application. Since the view also obtains this AppDelegate instance via a call to + sharedApplication there's no hint to users of this view that it has this dependency.
Consider instead allowing classes which use this view class to provide it with the data it should display.
If the property where addBalanceListener: stores the reference is weak, you don't need to do anything. Weak properties are automatically set to nil when the instance they are pointing to is dealloced.

A UIlabel issue: Keep the string value after app is closed?

So one of the users in here managed to show me how to pass data from a child view controller to a parent view controller via a string.
So now the string is passed, BUT, i want that value to stay displayed on the firstViewController after the app is closed and re-opened.
The value is saved in with NSUserDefaults by the way and with an NSLog i am seeing on the conosole it is saved in the apps folder but that value isnt saved onto the UILabel display.
It only displays it when i put save but then i close and reopen, it dissappears but in an NsLog it is still inside the app but not on display UILabel.
How can i address this ?
On my appDelegate.h i have a
#property (strong, nonatomic) NSString *sharedString;
To pass the secondViewController data to the firstViewController.
In the save method on my secondViewController i have a function related to the
AppDelegate.h declaration which is:
AppDelegate *apiDelegate = [[UIApplication sharedApplication] delegate]
apiDelegate.sharedString = self.textFieldData.text;
And in my firstViewController i have a method which display the data from the second
viewController:
-(void) viewDidAppear:(BOOL)animated {
AppDelegate *apiDelegate = [[UIApplication sharedApplication] delegate]
self.DisplayData.text = appDelegate.sharedString;
[super viewDidAppear: NO];
Is there something wrong which isnt keeping the data intact after app closes or am
I missing something here ?
So one of the users in here managed to show me how to pass data from a
child view controller to a parent view controller via a string.
First you need to establish some hierarchy as to how you get a childViewController from a parentViewController. One way to pass data from childViewController to parentViewController is using a delegate. The other could be using the KVC/KVO protocol. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html
In this you can simply register an observer for the property defined in the childViewController and observe it's changes wherever you want (well, given the hierarchy is satisfied).
To save the value. You can simply save it using NSUserDefaults. I don't see any code in your post but you can simply define a key and save the value with NSUserDefaults using:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:sharedString forKey:#"sharedString"];
NSString *sharedStringFromDefaults = [defaults objectForKey:#"sharedString"];
Also,
AppDelegate *apiDelegate = [[UIApplication sharedApplication]
delegate]
Apple requires you to avoid such references in the application. It only constrains the app. Further, the sharedString is not required to be in the AppDelegate. Otherwise the AppDelegate will be filled with almost every other data structure you have shared in the app.
//add this code when you want to store string
[[NSUserDefaults standardUserDefaults] setObject:self.textFieldData.text forKey:#"sharedString"];
[[NSUserDefaults standardUserDefaults] synchronize];
//and when you want string than
self.DisplayData.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"sharedString"];

Controlling the screenshot in the iOS 7 multitasking switcher

I've been trying to find some information regarding the new multitasking switcher in iOS 7 and especially the screenshot that the OS takes when the app is going into hibernation.
Is there any way to completely turn off this feature or screenshot? Or can I hide the app altogether from the switcher? The app needs to run in the background, but we do not want to show any screenshot from the app.
The screenshot is potentially a security-risk, think along the lines for banking-apps where your card number or account summary will be available to anyone that double-click on the home button on the device.
Anyone with any insight into this? Thanks.
In Preparing Your UI to Run in the Background, Apple says:
Prepare Your UI for the App Snapshot
At some point after your app enters the background and your delegate method returns, UIKit takes a snapshot of your app’s current user interface. The system displays the resulting image in the app switcher. It also displays the image temporarily when bringing your app back to the foreground.
Your app’s UI must not contain any sensitive user information, such as passwords or credit card numbers. If your interface contains such information, remove it from your views when entering the background. Also, dismiss alerts, temporary interfaces, and system view controllers that obscure your app’s content. The snapshot represents your app’s interface and should be recognizable to users. When your app returns to the foreground, you can restore data and views as appropriate.
See Technical Q&A QA1838: Preventing Sensitive Information From Appearing In The Task Switcher
In addition to obscuring/replacing sensitive information, you might also want to tell iOS 7 to not take the screen snapshot via ignoreSnapshotOnNextApplicationLaunch, whose documentation says:
If you feel that the snapshot cannot correctly reflect your app’s user interface when your app is relaunched, you can call ignoreSnapshotOnNextApplicationLaunch to prevent that snapshot image from being taken.
Having said that, it appears that the screen snapshot is still taken and I have therefore filed a bug report. But you should test further and see if using this setting helps.
If this was an enterprise app, you might also want to look into the appropriate setting of allowScreenShot outlined in the Restrictions Payload section of the Configuration Profile Reference.
Here is an implementation that achieves what I needed. You can present your own UIImageView, or your can use a delegate-protocol pattern to obscure the confidential information:
// SecureDelegate.h
#import <Foundation/Foundation.h>
#protocol SecureDelegate <NSObject>
- (void)hide:(id)object;
- (void)show:(id)object;
#end
I then gave my app delegate a property for that:
#property (weak, nonatomic) id<SecureDelegate> secureDelegate;
My view controller sets it:
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
delegate.secureDelegate = self;
}
The view controller obviously implements that protocol:
- (void)hide:(id)object
{
self.passwordLabel.alpha = 0.0;
}
- (void)show:(id)object
{
self.passwordLabel.alpha = 1.0;
}
And, finally, my app delegate avails itself of this protocol and property:
- (void)applicationWillResignActive:(UIApplication *)application
{
[application ignoreSnapshotOnNextApplicationLaunch]; // this doesn't appear to work, whether called here or `didFinishLaunchingWithOptions`, but seems prudent to include it
[self.secureDelegate hide:#"applicationWillResignActive:"]; // you don't need to pass the "object", but it was useful during my testing...
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self.secureDelegate show:#"applicationDidBecomeActive:"];
}
Note, I'm using applicationWillResignActive rather than the advised applicationDidEnterBackground, because, as others have pointed out, the latter is not called when double tapping on the home button while the app is running.
I wish I could use notifications to handle all of this, rather than the delegate-protocol pattern, but in my limited testing, the notifications aren't handled in a timely-enough manner, but the above pattern works fine.
This is the solution I worked with for my application:
As Tommy said: You can use the applicationWillResignActive. What I did was making a UIImageView with my SplashImage and add it as subview to my main window-
(void)applicationWillResignActive:(UIApplication *)application
{
imageView = [[UIImageView alloc]initWithFrame:[self.window frame]];
[imageView setImage:[UIImage imageNamed:#"Portrait(768x1024).png"]];
[self.window addSubview:imageView];
}
I used this method instead of applicationDidEnterBackground because applicationDidEnterBackground won't be triggered if you doubletap the home button, and applicationWillResignActive will be. I heard people say though it can be triggered in other cases aswell, so I'm still testing around to see if it gives problem, but none so far! ;)
Here to remove the imageview:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(imageView != nil) {
[imageView removeFromSuperview];
imageView = nil;
}
}
Hope this helps!
Sidenote: I tested this on both the simulator and a real device: It Won't Show on the simulator, but it does on a real device!
This quick and easy method will yield a black snapshot above your app's icon in the iOS7 or later app switcher.
First, take your app's key window (typically setup in AppDelegate.m in application:didFinishLaunchingWithOptions), and hide it when your app is about to move into the background:
- (void)applicationWillResignActive:(UIApplication *)application
{
if(isIOS7Or8)
{
self.window.hidden = YES;
}
}
Then, un-hide your app's key window when your app becomes active again:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(isIOS7Or8)
{
self.window.hidden = NO;
}
}
At this point, check out the app switcher and verify that you see a black snapshot above your app's icon. I've noticed that if you launch the app switcher immediately after moving your app into the background, there can be a delay of ~5 seconds where you'll see a snapshot of your app (the one you want to hide!), after which it transitions to an all-black snapshot. I'm not sure what's up with the delay; if anyone has any suggestions, please chime in.
If you want a color other than black in the switcher, you could do something like this by adding a subview with any background color you'd like:
- (void)applicationWillResignActive:(UIApplication *)application
{
if(isIOS7Or8)
{
UIView *colorView = [[[UIView alloc] initWithFrame:self.window.frame] autorelease];
colorView.tag = 9999;
colorView.backgroundColor = [UIColor purpleColor];
[self.window addSubview:colorView];
[self.window bringSubviewToFront:colorView];
}
}
Then, remove this color subview when your app becomes active again:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(isIOS7Or8)
{
UIView *colorView = [self.window viewWithTag:9999];
[colorView removeFromSuperview];
}
}
I used the following solution:
when application is going to resign I get appWindow snapshot as a View and add blur to it. Then I add this view to app window
how to do this:
in appDelegate just before implementation add line:
static const int kNVSBlurViewTag = 198490;//or wherever number you like
add this methods:
- (void)nvs_blurPresentedView
{
if ([self.window viewWithTag:kNVSBlurViewTag]){
return;
}
[self.window addSubview:[self p_blurView]];
}
- (void)nvs_unblurPresentedView
{
[[self.window viewWithTag:kNVSBlurViewTag] removeFromSuperview];
}
#pragma mark - Private
- (UIView *)p_blurView
{
UIView *snapshot = [self.window snapshotViewAfterScreenUpdates:NO];
UIView *blurView = nil;
if ([UIVisualEffectView class]){
UIVisualEffectView *aView = [[UIVisualEffectView alloc]initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
blurView = aView;
blurView.frame = snapshot.bounds;
[snapshot addSubview:aView];
}
else {
UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:snapshot.bounds];
toolBar.barStyle = UIBarStyleBlackTranslucent;
[snapshot addSubview:toolBar];
}
snapshot.tag = kNVSBlurViewTag;
return snapshot;
}
make your appDelegate implementation be the as follows:
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
//...
//your code
//...
[self nvs_blurPresentedView];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
//...
//your code
//...
[self nvs_unblurPresentedView];
}
I created Example projects in Swift and Objective C.
Both projects makes the following actions in:
-application:didResignActive - snapshot is created, blurred and added to app window
-application:willBecomeActive blur view is being removed from window.
How to use:
Objecitve C
Add AppDelegate+NVSBlurAppScreen .h and .m files to your project
in your -applicationWillResignActive: method add the following line:
[self nvs_blurPresentedView];
in your -applicationDidEnterBackground: method add the following line:
[self nvs_unblurPresentedView];
Swift
add AppDelegateExtention.swift file to your project
in your applicationWillResignActive function add the following line:
blurPresentedView()
in your applicationDidBecomeActive function add the following line:
unblurPresentedView()
if only use [self.window addSubview:imageView]; in applicationWillResignActive function, This imageView won't cover UIAlertView, UIActionSheet or MFMailComposeViewController...
Best solution is
- (void)applicationWillResignActive:(UIApplication *)application
{
UIWindow *mainWindow = [[[UIApplication sharedApplication] windows] lastObject];
[mainWindow addSubview:imageView];
}
Providing my own solution as an "answers", though this solution is very unreliable. Sometimes i get a black screen as the screenshot, sometimes the XIB and sometimes a screenshot from the app itself. Depending on device and/or if i run this in the simulator.
Please note i cannot provide any code for this solution since it's a lot of app-specific details in there. But this should explain the basic gist of my solution.
In AppDelegate.m under applicationWillResignActive i check if we're
running iOS7, if we do i load a new view which is empty with the
app-logo in the middle. Once applicationDidBecomeActive is called i
re-launch my old views, which will be reset - but that works for the
type of application i'm developing.
You can use activator to configure double clicking of home button to launch multitasking and disable default double clicking of home button and launching of multitasking window. This method can be used to change the screenshots to the application's default image. This is applicable to apps with default passcode protection feature.
Xamarin.iOS
Adapted from https://stackoverflow.com/a/20040270/7561
Instead of just showing a color I wanted to show my launch screen.
public override void DidEnterBackground(UIApplication application)
{
//to add the background image in place of 'active' image
var backgroundImage = new UIImageView();
backgroundImage.Tag = 1234;
backgroundImage.Image = UIImage.FromBundle("Background");
backgroundImage.Frame = this.window.Frame;
this.window.AddSubview(backgroundImage);
this.window.BringSubviewToFront(backgroundImage);
}
public override void WillEnterForeground(UIApplication application)
{
//remove 'background' image
var backgroundView = this.window.ViewWithTag(1234);
if(null != backgroundView)
backgroundView.RemoveFromSuperview();
}

Refresh a view in UINavigationController Stack

I am currently building my view in the viewDidLoad method, it adds various buttons that links through to another view. The issue I am having is that when a user clicks on a button and goes through to the other view they can purchase an IAP, then when the user clicks on the back button I would like the view to 'refresh' to show that this purchase has now become active.
How do I refresh the view?
Thanks in advance
any view can be explicitly refreshed by calling setNeedsDisplay on that view.
Further you should not mix up the terms UIViewController with UIView.
I asume you dont have a refresh problem, that you would only have in self programmed custom views. I excpect that the data of the related views was not updated.
You have a few options. Either store whether the purchase was made and then run the method that checks if a purchase was made in the viewWillAppear method of the view you are wishing to change
Or...
Setup a delegate callback that changes the button when a change is made on the purchase page (http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-custom-delegates/)
Or...
Manipluate the previous view directly by accessing it from the navigationController stack ([[self.navigationController viewControllers]objectAtIndex:YOURVIEW])
build view in viewDidLoad so you add controls only once, but refresh in viewWillShow. Keep in mind, viewWillShow will refresh it right after the build is done by viewDidLoad. So keep values that define your controls appearance on the 1st viewController, say, in delegate mutable array
e.g. create and initiate an NSMutableArray in your AppDelegate.h and .m
in .h interface
NSMutableArray *yourArray;
add property
#property (retain, nonatomic) NSMutableArray *yourArray;
in .m
#synthesyze yourArray;
initiate it in .m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
yourArray=[[NSMutableArray alloc]init];
//add initial objects to it as many as you have controls
}
in the 1st viewController
#import "AppDelegate.h"
-(void)viewWillShow:(BOOL)animated{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//pull objects from array and apply to your controls, say you have a string as a 1st obj
NSString *tmpStr=[appDelegate.yourArray objectAtIndex:0];//use it some where, say button's title
//etc
}
you will change those values in the second viewController according to users choices
#import "AppDelegate.h"
somewhere in 2nd viewController code update values in yourArray say on a
-(void)viewWillDisappear:(BOOL)animated{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//run a loop and update all objects
[appDelegate.yourArray replaceObjectAtIndex:i withObject:newObj];
}
and when you go back to 1st viewController, viewWillShow will refresh them from your delegate array.

Resources