Aroundabouts the internet, I've come across the following code for stretching an image to fill a UIView:
UIGraphicsBeginImageContext(self.view.frame.size);
[[UIImage imageNamed:#"image.png"] drawInRect:self.view.bounds];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.view.backgroundColor = [UIColor colorWithPatternImage:image];
This is all well and good. But I thought I'd repurpose the code to work for a custom UIView class being used as a subview, and put the following code in initWithCoder: (since the views are being added in storyboard) as follows:
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
UIGraphicsBeginImageContext(self.frame.size);
[[UIImage imageNamed:#"image.png"] drawInRect:self.bounds];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.backgroundColor = [UIColor colorWithPatternImage:image];
}
return self;
}
Note that I've changed "self.view" to simply "self" to reflect that this code is being called by the view itself and not by a view controller. The first code sample works great, and fills the view grandly. The second doesn't (even if I put it in the view controller ala self.subView.frame.size), and I have a feeling it's because I don't properly understand frame and bounds. Can somebody give me a quick crash course, or (if I'm way off base) point out what's actually wrong?
EDIT: Now I'm really confused. Using NSLog, I've gleaned the following information:
self.view.bounds.size.width: 768
self.subView.bounds.size.width: 0
...Huh?? The same is true of frame. But... it's on there, it's got dimensions...
I think there might be an easier way to accomplish what you are wanting to do. If I understand you correctly, you're looking to have image.png be a background image for your custom view. If so, all you'll need to do is add a UIImageView when you initialize your custom view.
- (id) initWithCoder: (NSCoder *) coder
{
self = [super initWithCoder: coder];
if (self) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame: self.bounds];
[imageView setAutoresizingMask: (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth)];
[imageView setImage: [UIImage imageNamed: #"image.png"]];
[self addSubview: imageView];
}
return self;
}
Related
I have the following code in my viewWillAppear in a modal uiviewcontroller.
I am including UIImage+ImageEffects.h to do the blurring of the background image in this example.
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:NO];
// grab an image of our parent view
UIView *parentView = self.presentingViewController.view;
UIImage *parentViewImage = [self takeSnapshotOfView:parentView];
UIImage *blurredImage = nil;
//BLUR THE IMAGE
blurredImage = [self blurWithImageEffects:parentViewImage];
// insert an image view with a picture of the parent view at the back of our view's subview stack...
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
imageView.image = blurredImage;
[self.view insertSubview:imageView atIndex:0];
}
[EDIT] Adding Blur Methods
- (UIImage *)takeSnapshotOfView:(UIView *)view
{
CGFloat reductionFactor = 1.5;
UIGraphicsBeginImageContext(CGSizeMake(view.frame.size.width/reductionFactor, view.frame.size.height/reductionFactor));
[view drawViewHierarchyInRect:CGRectMake(0, 0, view.frame.size.width/reductionFactor, view.frame.size.height/reductionFactor) afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (UIImage *)blurWithImageEffects:(UIImage *)image
{
return [image applyBlurWithRadius:10 tintColor:[UIColor colorWithWhite:1 alpha:0.2] saturationDeltaFactor:1.5 maskImage:nil];
}
The code works fine and the background shows up blurry but it is really noticeably slow on iPad 3 with iOS 8. When the button to display this viewcontroller is tapped there is a pause before the viewcontroller slides up from the bottom. If I remove the blur the view controller slides up faster.
I tried putting the code in the viewDidAppear but then there is a noticeable white background displayed for a few seconds before the blur appears. But when in viewDidAppear the viewcontroller slides up immediately.
If I reduce the applyWithBlurRadius value this does not seem to reduce the time it takes to apply the blur.
Is there any way I can make it run faster?
GPUImage might solve your issue, Its fast and it doesn't put too much load on processing.
Link To Framework : https://github.com/BradLarson/GPUImage
Link to use GPUImage Blur
http://blog.bubbly.net/tag/gpu-image/
http://www.raywenderlich.com/60968/ios-7-blur-effects-gpuimage
However a reference is shown below which will give you the basic idea:
First prepare your view which is to blurred:
- (NSData *)PhotoForBlurring:(UIView *)view{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 1.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * data = UIImagePNGRepresentation(image);
[data writeToFile:#"aName.png" atomically:YES];
return data; }
-(void)sharingScrapPage{
GPUImageiOSBlurFilter *blur = [[GPUImageiOSBlurFilter alloc]init];
blur.blurRadiusInPixels=2.0;
blur.downsampling=6.0;
[blurview removeFromSuperview];
blurview = [[UIImageView alloc]initWithFrame:getRectDisplay(0, 0, 480, 320)]; // Landscape Iphone 4S
UIImage *imageForBlurring=[UIImage imageWithData:[self PhotoForBlurring:self.view]];
NSData* pngdataForBlurring = UIImagePNGRepresentation (imageForBlurring);
UIImage* blurImage = [UIImage imageWithData:pngdataForBlurring];
blurImage=[blur imageByFilteringImage:blurImage];
blurview.image=blurImage;
[self.view addSubview:blurview];
[progressHUD removeFromSuperview];
progressHUD = [[ProgressHUD alloc]initWithFrame:getRectDisplay(190, 110, 100, 90)];
[blurview addSubview:progressHUD]; }
I am implementing UIViewcontroller containment. In the example below I set the frame size of the childcontrollers in the rootcontroller. The child view appears as the size I have set, however when I check on its bounds within container1 it reports a different size to the size I set.
Rootcontroller (container)
- (void)viewDidLoad
{
[super viewDidLoad];
self.containA = [[Container1 alloc]init];
self.containB = [[Container2 alloc]init];
self.containA.view.frame = CGRectMake(50, 50,50, 50);
self.containB.view.frame = CGRectMake(50, 50, 300, 300);
[self addChildViewController:self.containA];
[self addChildViewController:self.containB];
[self.view addSubview:self.containA.view];
Container1
-(void)viewDidLoad {
[super viewDidLoad];
UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
//[self.view addSubview:view];
self.view = view;
self.view.backgroundColor = [UIColor redColor];
[view release];
NSLog(#"view dims %f %f",self.view.bounds.size.width,self.view.bounds.size.height);
}
Console output from container 1
view dims 768.000000 1004.000000
I thought the issue was related to setting the view frame to UIScreen main screen]applicationFrame .So I removed all of this code so the uiview is created automatically. The issue still remains..
View frames are not actually useable in viewDidLoad; you should move all of your geometry-manipulating code into viewWillAppear. The system will clobber any changes you set in viewDidLoad between there and when it's about tom come on screen.
So I've read this post
But it doesn't seem to deal with the issue of making the background NOT tint to a semi-transparent. I want the content behind the menu with buttons to be clearly visible. Is this even possible?
This is what I have currently
WPSActionSheet *actionSheet = [[WPSActionSheet alloc] initWithCompletion:completion];
[actionSheet setTitle:#""];
[actionSheet addButtonWithTitle:#"Take Photo"];
[actionSheet addButtonWithTitle:#"Choose from Library"];
[actionSheet addButtonWithTitle:#"Skip This Step"];
[actionSheet setCancelButtonIndex:2];
[actionSheet showInView:[self view]];
which shows this
UIActionSheet will always show the "tinted background". There is no way around this. You can however create your own version of the UIActionSheet using a UIView.
Just set the alpha and opaque property of UIActionSheet with delegate method -
(void)willPresentActionSheet:(UIActionSheet *)actionSheet
and it will be visible as you want.
If you want to make background of UIActionSheet transparent you can subclass it and change layoutSubviews
- (void)layoutSubviews
{
[super layoutSubviews];
UIImage* image;
CGSize size = self.frame.size;
UIGraphicsBeginImageContext(size);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[self layer] setContents:(id)image.CGImage];
}
To add some custom background just add following code to layoutSubviews:
CGRect frame = self.frame;
frame.origin = CGPointZero;
UIImageView* bg = [[UIImageView alloc] initWithFrame:frame];
bg.image = [UIImage imageNamed#"YOUR/IMAGE/NAME"];
[self addSubview:bg];
[self sendSubviewToBack:bg];
Also you can do all those actions in delegate method
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet
simply replacing self to actionSheet.
I had the same problem and found a workaround. The UIActionSheet's superview contains a UIView that is used for the tinting. So you can remove the tinting by setting the opacity of this view's layer to 0.
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet {
NSArray *subviews = actionSheet.superview.subviews;
UIView *tintView = [subviews objectAtIndex:0];
[tintView.layer setOpacity:0.0];
}
I'm using the code found here http://blog.blackwhale.at/?p=336 to create a custom activityIndicator in the current version of xCode. This code uses a series of .png images in an array and then displays them at the set interval to create an animated loading image that you can then place on the screen (and assuming you can somehow remove it) whenever you want.
In my main ViewController.m file I have the following code in my viewDidLoad section :
/* --- START CUSTOM ACTIVITY INDICATOR */
//Create the first status image and the indicator view
UIImage *statusImage = [UIImage imageNamed:#"activity1.png"];
UIImageView *activityImageView = [[UIImageView alloc]
initWithImage:statusImage];
//Add more images which will be used for the animation
activityImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"activity1.png"],
[UIImage imageNamed:#"activity2.png"],
[UIImage imageNamed:#"activity3.png"],
[UIImage imageNamed:#"activity4.png"],
[UIImage imageNamed:#"activity5.png"],
[UIImage imageNamed:#"activity6.png"],
[UIImage imageNamed:#"activity7.png"],
[UIImage imageNamed:#"activity8.png"],
[UIImage imageNamed:#"activity9.png"],
[UIImage imageNamed:#"activity10.png"],
[UIImage imageNamed:#"activity11.png"],
[UIImage imageNamed:#"activity12.png"],
nil];
//Set the duration of the animation (play with it
//until it looks nice for you)
activityImageView.animationDuration = 0.6;
//Position the activity image view somewhere in
//the middle of your current view
activityImageView.frame = CGRectMake(
self.view.frame.size.width/2
-statusImage.size.width/2,
self.view.frame.size.height/1.2
-statusImage.size.height/1.2,
statusImage.size.width,
statusImage.size.height);
//Start the animation
[activityImageView startAnimating];
//Add your custom activity indicator to your current view
[self.view addSubview:activityImageView];
/* --- END CUSTOM ACTIVITY INDICATOR */
I would like to CLEAR/DELETE/HIDE the activityImageView element, as I only want it to show when the app first launches until the UIWebView finishes loading.
I would like to call this and have the gif appear while webViewDidStartLoad and then clear when webViewDidFinishLoad. Someone help??
You should go like this:
- (void)viewDidLoad {
[super viewDidLoad];
//Create the first status image and the indicator view
UIImage *statusImage = [UIImage imageNamed:#"activity1.png"];
UIImageView *activityImageView = [[UIImageView alloc]
initWithImage:statusImage];
//Add more images which will be used for the animation
activityImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"activity1.png"],
[UIImage imageNamed:#"activity2.png"],
[UIImage imageNamed:#"activity3.png"],
[UIImage imageNamed:#"activity4.png"],
[UIImage imageNamed:#"activity5.png"],
[UIImage imageNamed:#"activity6.png"],
[UIImage imageNamed:#"activity7.png"],
[UIImage imageNamed:#"activity8.png"],
[UIImage imageNamed:#"activity9.png"],
[UIImage imageNamed:#"activity10.png"],
[UIImage imageNamed:#"activity11.png"],
[UIImage imageNamed:#"activity12.png"],
nil];
//Set the duration of the animation (play with it
//until it looks nice for you)
activityImageView.animationDuration = 0.6;
//Position the activity image view somewhere in
//the middle of your current view
activityImageView.frame = CGRectMake(
self.view.frame.size.width/2
-statusImage.size.width/2,
self.view.frame.size.height/1.2
-statusImage.size.height/1.2,
statusImage.size.width,
statusImage.size.height);
//Start the animation
[activityImageView startAnimating];
//Add your custom activity indicator to your current view
[self.view addSubview:activityImageView];
self.timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0)
target:self
selector:#selector(loading)
userInfo:nil
repeats:YES];
}
- (void)loading {
if (!self.webView.loading) {
[activityImageView stopAnimating];
activityImageView.hidden = 1;
} else {
[activityImageView startAnimating];
}
}
Okay, so the reason that you cannot access activityImageView is because its declared in your viewDidLoad function. Add it as a property to your ViewController class and you should be able to do what you want. Make sure that you remove the declaration of activityImageView from viewDidLoad, and just initialize it there.
Also, where is your webview that you want to show the activity indicator? Does it have a separate view controller or is it included in your main view controller? It sounds like your scope for activityImageView is incorrect, though without knowing how you have everything set up its a little hard for me to tell you where to put it.
After you use your activity indicator in these two places, do you plan on using it again during the applications run time, or will it only ever be visible if you reload the application? Depending on the answer to that you will either want to just hide it, or remove it from you main view.
UIImageView.hidden is what controls visibility.
EDIT, Code as requested:
ViewController.h
#interface ViewController : UIViewController
{
UIWebView *_webView;
}
#property(nonatomic, strong) UIWebView *webView;
#property(nonatomic, strong) UIImageView *activityImageView;
ViewController.m
#implementation ViewController
#synthesize activityImageView;
#synthesize webView = _webView;
-(void)viewDidLoad {
//all your previous stuff with the change that you just alloc activityImageView instead of declare it
activityImageView = [[UIImageView alloc] initWithImage:statusImage];
//The above is the initialization, below is where your old code should go
}
Declare your activityImageView as a property in your header file and configure it in your viewDidLoad: method
You should add your activityImageView in UIWebViewDelegate method
- (void)webViewDidStartLoad:(UIWebView *)webView
And then stopAnimating and removeFromSuperview in the UIWebViewDelegate methods
- (void)webViewDidFinishLoad:(UIWebView *)webView
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
You should read UIImageView documentation and the UIWebViewDelegate protocol reference
UPDATE
In your header:
#property(nonatomic, retain) UIImageView *activityImageView;
In your implementation
#synthesize activityImageView;
-(void)viewDidLoad{
activityImageView = [[UIImageView alloc]
initWithImage:statusImage];
//Add more images which will be used for the animation
activityImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"activity1.png"],
[UIImage imageNamed:#"activity2.png"],
[UIImage imageNamed:#"activity3.png"],
[UIImage imageNamed:#"activity4.png"],
[UIImage imageNamed:#"activity5.png"],
[UIImage imageNamed:#"activity6.png"],
[UIImage imageNamed:#"activity7.png"],
[UIImage imageNamed:#"activity8.png"],
[UIImage imageNamed:#"activity9.png"],
[UIImage imageNamed:#"activity10.png"],
[UIImage imageNamed:#"activity11.png"],
[UIImage imageNamed:#"activity12.png"],
nil];
//Set the duration of the animation (play with it
//until it looks nice for you)
activityImageView.animationDuration = 0.6;
//Position the activity image view somewhere in
//the middle of your current view
activityImageView.frame = CGRectMake(
self.view.frame.size.width/2
-statusImage.size.width/2,
self.view.frame.size.height/1.2
-statusImage.size.height/1.2,
statusImage.size.width,
statusImage.size.height);
}
- (void)webViewDidStartLoad:(UIWebView *)webView{
[self.activityImageView startAnimating];
[self.view addSubview:activityImageView];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{
[activityImageView stopAnimating];
[activityImageView removeFromSuperview];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
[activityImageView stopAnimating];
[activityImageView removeFromSuperview];
}
I've a sample project, where I have created a custom UIWebView named WebView. They are added to the view in a UIViewController. Two WebView's are initialized in the viewDidLoad and added to an array list. The first one is added as subview på self.view. The Storyboard contains a UIBarButton, and when this is tapped, the second WebView is added as subview to the self.view.
- (void)viewDidLoad
{
[super viewDidLoad];
WebView *webView1 = [[WebView alloc] initWithFrame:self.view.bounds];
WebView *webView2 = [[WebView alloc] initWithFrame:self.view.bounds];
self.webViews = [NSArray arrayWithObjects: webView1, webView2, nil];
UIWebView *webView = [self.webViews objectAtIndex:0];
[self.view addSubview:webView];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://m.google.com/"]]];
}
Here is the implementation of the WebView where a toolbar is added to the bottom (-50px) of the view:
#implementation WebView
- (void) initToolbar {
UIToolbar *toolbar = [UIToolbar new];
toolbar.barStyle = UIBarStyleBlackTranslucent;
toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
[toolbar sizeToFit];
toolbar.frame = CGRectMake(0, self.frame.size.height-80, 320, 30);
[self addSubview:toolbar];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self initToolbar];
}
return self;
}
#end
The problem is, that the toolbar does not have the same position in the two views (they could be positioned like the first one). Why are they not positioned equally?
You can download the small sample project here:
http://uploads.demaweb.dk/UIWebView.zip
My notes: As mentioned, the two WebView's are currently initialized in the viewDidLoad. If I instead wait and first initialize then when I need them, it seems to work as expected.
The thing here is that self.frame.size.height on viewDidLoad is not the same when you initialize it via the button click. If you add the UIWebView directly in viewDidLoad, the UINavigationBar of your UINavigationController is not loaded yet and the frame of self.view is 'perceived' to be (not really, because it is) larger.
This explains the difference of 40.0 (?) points.
One possible solution to this problematic behaviour is to initialize and add (at least the first) instance of UIWebView in the viewWillAppear:animated method. (UIViewController does already implement this method)