My main aim is to have one background for all of my ViewControllers. Every ViewController I have has a clear background.
To do this, I have made one UIViewController (called backgroundViewController) that will act as the subview to all my other ViewControllers. It has one UIImageView which displays this particular background. I will then add this backgroundViewController as a subview of all my other ViewControllers.
The problem is - this imageView won't show as a subview!
This is how I display the imageView:
- (void)viewDidLoad
{
if ([musicPlayer playbackState] == MPMusicPlaybackStateStopped) {
UIImage *artworkBackgroundView;
artworkBackgroundView = [UIImage imageNamed:#"noArtworkBackground"];
UIImage *effectImage = nil;
backgroundView.image = effectImage;
[backgroundView setImage:artworkBackgroundView];
backgroundView.image = effectImage;
}
}
- (void) handle_NowPlayingItemChanged: (id) notification
{
if ([musicPlayer playbackState] != MPMusicPlaybackStateStopped) {
// Get artwork for current now playing item
MPMediaItem *currentItem = [musicPlayer nowPlayingItem];
MPMediaItemArtwork *artwork = [currentItem valueForProperty: MPMediaItemPropertyArtwork];
UIImage *artworkBackgroundView = [artwork imageWithSize: CGSizeMake(618, 618)];
if (!artworkImage) {
artworkBackgroundView = [UIImage imageNamed:#"noArtworkBackground"];
artworkImage = [UIImage imageNamed:#"noArtwork"];
}
[backgroundView setImage:artworkBackgroundView];
}
}
As you can see, backgroundView changes each time the music player skips song.
To test that backgroundViewController does show as a subview, I added another imageView and changed its image to a static .png in Interface Builder, and that shows correctly as a subview.
This is how I make it a subview for other ViewControllers:
backgroundViewController *backgroundVC = [[backgroundViewController alloc] initWithNibName:#"backgroundViewController" bundle:nil];
[self.view insertSubview:backgroundVC.view atIndex:0];
What I want is the UIImageView called backgroundView to show up when it is being called as subview.
I have tested that backgroundView does change according to what song is playing, and it works correctly.
What am I doing wrong? backgroundView refuses to show up as a subview?! I've searched a ton about adding ViewControllers as subviews but I can't find a similar problem.
Any help would be much appreciated, thanks! :)
This worked for me:
backgroundViewController *backgroundVC = [[backgroundViewController alloc] initWithNibName:#"backgroundViewController" bundle:nil];
[self addChildViewController:backgroundVC];
[self.view addSubview:backgroundVC.view];
I found the solution here. I need to read more about 'Custom Containers'.
Related
This is what I did:
#implementation BGUIActivityIndicator
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
[self customInitialize];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
[self customInitialize];
return self;
}
-(void)customInitialize
{
UIView * theImageView= [self findASubViewforClass:[UIImageView class]];//
UIImageView * theImageView1 = (UIImageView *) theImageView;
theImageView1.image = [UIImage imageNamed:#"spinner_blue"];
[theImageView1.image saveScreenshot];
while (false) ;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
Everything seems perfect. [theImageView1.image saveScreenshot]; jot down both the old and new view perfectly.
However, nothing changes. Why?
I am not exactly asking how to change the image of UIActivityIndicator. There are tons of it already. I want to use it by subclassing UIActivityIndicator because I think it's the most elegant solution. I can't seem to do that.
In particular, I am asking why my approach, which works for changing background of search controller, for example, doesn't work for this.
According to the UIActivityIndicatorView Class Reference ,there is no way/ chance to change the image through sub-classing.
However you can change its activityIndicatorViewStyle , color of the activity indicator,UIActivityIndicatorStyle etc..
I think, without sub-classing, the class class UIImageView provides a very useful and simple way to implement such a thing. The only thing you have to do is to:
1.Provide a number of images that reflect your indicator animation.
2.Create a new UIImageView instance and set images and animation duration.
3.Position your custom activity indicator within your current view.
SAMPLE CODE:
//Create the first status image and the indicator view
UIImage *statusImage = [UIImage imageNamed:#"status1.png"];
UIImageView *activityImageView = [[UIImageView alloc]
initWithImage:statusImage];
//Add more images which will be used for the animation
activityImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"status1.png"],
[UIImage imageNamed:#"status2.png"],
[UIImage imageNamed:#"status3.png"],
[UIImage imageNamed:#"status4.png"],
[UIImage imageNamed:#"status5.png"],
[UIImage imageNamed:#"status6.png"],
[UIImage imageNamed:#"status7.png"],
[UIImage imageNamed:#"status8.png"],
nil];
//Set the duration of the animation (play with it
//until it looks nice for you)
activityImageView.animationDuration = 0.8;
//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/2
-statusImage.size.height/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];
See the full details Here
For the first time working with CorePlot (after a couple of hours trying to set it up :P )
on my view, i have a tableview. when a certain IBAction is called i want to show another view (a graph) instead of the tableview.
my approach was to add a subview with the same size to the tableview. it works fine to display the graph, but when i remove the graphs view from [table subviews] the tableview does not reappear.
note:
expenseTable: my tableView
hasSubView: (BOOL) that indicates if a graph is shown right now or not
code
-(IBAction)displayDayBalanceGraph:(id)sender{
if (hasSubView) {
[[expenseTable subviews] makeObjectsPerformSelector: #selector(removeFromSuperview)];
NSLog(#"%#",expenseTable.subviews);
}
else{
[self initializeMonthArray];
CPTGraphHostingView *host = [self buildGraphView];
[expenseTable addSubview:host];
CPTXYGraph *graph = [[CPTXYGraph alloc ]initWithFrame:host.frame];
host.hostedGraph = graph;
CPTScatterPlot *plot = [[CPTScatterPlot alloc]init ];
plot.dataSource = self;
[graph addPlot:plot];
[expenseTable reloadData];
hasSubView = !hasSubView;
}
}
-(CPTGraphHostingView *)buildGraphView{
CPTGraphHostingView *view = [[CPTGraphHostingView alloc]initWithFrame:CGRectMake(0, 0, 312, 260)];
[view setBackgroundColor:[self grayColor]];
return view;
}
1st Screenshot: TableView displayed
2nd Screenshot: GraphView displayed
sidenote: this is a sampleplot =)
3rd Screenshot: GraphView dismissed
has anyone an idea what i missed? (or messed ;) )
It's not generally a good idea to add views as subviews of UITableView.
Instead, you could either remove the table view and replace it with the Core Plot view:
[tableView removeFromSuperview];
[containerView addSubview:corePlotView];
Make sure you have a reference to the table view somewhere or it will be released.
I am developing an application that allows the user at a certain point to drag and drop 10 images around. So there are 10 images, and if he/she drags one image onto another, these two are swapped.
A screenshot of how this looks like:
So when the user drags one photo I want it to reduce its opacity and give the user a draggable image on his finger which disappears again if he drops it outside of any image.
The way I have developed this is the following. I have set a UIPanGesture for these UIImageViews as:
for (UIImageView *imgView in editPhotosView.subviews) {
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(photoDragged:)];
[imgView addGestureRecognizer:panGesture];
imgView.userInteractionEnabled = YES;
[panGesture release];
}
Then my photoDragged: method:
- (void)photoDragged:(UIPanGestureRecognizer *)gesture {
UIView *view = gesture.view;
if ([view isKindOfClass:[UIImageView class]]) {
UIImageView *imgView = (UIImageView *)view;
if (gesture.state == UIGestureRecognizerStateBegan) {
imgView.alpha = 0.5;
UIImageView *newView = [[UIImageView alloc] initWithFrame:imgView.frame];
newView.image = imgView.image;
newView.tag = imgView.tag;
newView.backgroundColor = imgView.backgroundColor;
newView.gestureRecognizers = imgView.gestureRecognizers;
[editPhotosView addSubview:newView];
[newView release];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [gesture translationInView:view.superview];
[view setCenter:CGPointMake(view.center.x + translation.x, view.center.y + translation.y)];
[gesture setTranslation:CGPointZero inView:view.superview];
}
else { ... }
}
}
Thus as you see I add a new UIImageView with 0.5 opacity on the same spot as the original image when the user starts dragging it. So the user is dragging the original image around. But what I want to do is to copy the original image when the user drags it and create a "draggable" image and pass that to the user to drag around.
But to do that I have to pass the user touch on to the newly created "draggable" UIImageView. While it's actually set to the original image (the one the user touches when he starts dragging).
So my question is: How do I pass the user's touch to another element?.
I hope that makes sense. Thanks for your help!
Well, you can pass the UIPanGestureRecognizer object to another object by creating a method in your other object which takes the gesture recognizer as a parameter.
- (void)myMethod:(UIPanGestureRecognizer *)gesture
{
// Do stuff
}
And call from your current gesture recognizer using....
[myOtherObject myMethod:gesture];
Not entirely sure I'm understanding your question here fully. :-/
Maybe:
[otherObject sendActionsForControlEvents:UIControlEventTouchUpInside];
Or any other UIControlEvent
In the end I decided to indeed drag the original image and leave a copy at the original place.
I solved the issue with the gesture recognizers I was having by re-creating them and assigning them to the "copy", just like PragmaOnce suggested.
I want to add functionality to my back buttons through my UINavigationController-based app where long-pressing the back button will pop to root. However, I can't figure out where to attach the gesture recognizer. Do I subclass UINavigationBar and try and detect if the long press is in the left button region?
I've heard of people adding similar functionality before. Anyone have any ideas?
I know this question is old, but I came up with a solution. Instead of trying to add the gesture recognizer to the button itself (which would be ideal), I added it to the self.navigationController.navigationBar and then in the action method, use the locationInView to see if I'm over the back button. I wasn't entirely sure about how to identify the back button precisely, so I'm clumsily just grabbing the the first subview with an x coordinate less than some arbitrary value, but it seems promising. If someone has a better way to identify the frame of the back button, let me know.
- (void)longPress:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
// set a default rectangle in case we don't find the back button for some reason
CGRect rect = CGRectMake(0, 0, 100, 40);
// iterate through the subviews looking for something that looks like it might be the right location to be the back button
for (UIView *subview in self.navigationController.navigationBar.subviews)
{
if (subview.frame.origin.x < 30)
{
rect = subview.frame;
break;
}
}
// ok, let's get the point of the long press
CGPoint longPressPoint = [sender locationInView:self.navigationController.navigationBar];
// if the long press point in the rectangle then do whatever
if (CGRectContainsPoint(rect, longPressPoint))
[self doWhatever];
}
}
- (void)addLongPressGesture
{
if (NSClassFromString(#"UILongPressGestureRecognizer"))
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[self.navigationController.navigationBar addGestureRecognizer:longPress];
[longPress release];
}
}
I believe UIGestureRecognizers can only be added to UIViews and subclasses of UIViews.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html
The back button is a UIBarButtonItem that descends from NSObject. Therefore, you won't be able to attach a gesture recognizer to a standard back button using
UILongPressGestureRecognizer *longPressGesture =
[[[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(longPress:)] autorelease];
[self.navigationItem.backBarButtonItem addGestureRecognizer:longPressGesture];
You can however add a custom view to a UIBarButtonItem. A custom view could just as easily be a UIView, UIButton, UILabel, etc.
Example:
UIView *myTransparentGestureView = [[UIView alloc] initWithFrame:CGRectMake(0,0,40,30)];
[myTransparentGestureView addGestureRecognizer:longPressGesture];
[self.navigationItem.backBarButtonItem setCustomView:myTransparentGestureView];
// Or you could set it like this
// self.navigationItem.backBarButtonItem.customView = myTransparentGestureView;
[myTransparentGestureView release];
You have to be careful however, since setting properties on backBarButtonItem applies to the next view that you push. So if you have view A that pushes to view B and you want the gesture to be recognized when you tap back in view B. You must set it up in view A.
I followed a slightly different path, figured I'd share it. The above answers are fine, but really, if the long press is in the leading 1/3 of the nav bar, that's good enough for me:
- (void)longPress:(UILongPressGestureRecognizer *)gr
{
NSLog(#"longPress:");
UINavigationBar *navBar = [self navigationBar];
CGFloat height = navBar.bounds.size.height;
CGPoint pt = [gr locationOfTouch:0 inView:navBar];
//NSLog(#"PT=%# height=%f", NSStringFromCGPoint(pt), height);
if(CGRectContainsPoint(CGRectMake(0,0,100,height), pt)) {
[self popToViewController:self.viewControllers[0] animated:YES];
}
}
Here's my solution:
In appDelegate (the "owner" of the nav bar in my app), In applicationDidFinishLaunchingWithOptions:
Get the nav bar view and add the gesture recognizer to the whole view:
// Get the nav bar view
UINavigationBar *myNavBar = nil;
for (UIView *view in [self.window.rootViewController.view subviews]) {
if ([view isKindOfClass:[UINavigationBar class]]) {
NSLog(#"Found Nav Bar!!!");
myNavBar = (UINavigationBar *)view;
}
}
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(backButtonLongPress:)];
[myNavBar addGestureRecognizer:longPress];
NSLog(#"Gesture Recognizer Added.");
Then in appDelegate, in -(void) backButtonLongPress:(id) sender
Check to see if the gesture occurs within the frame of the back button:
if ([sender state] == UIGestureRecognizerStateBegan) {
// Get the nav bar view
UINavigationBar *myNavBar = nil;
for (UIView *view in [self.window.rootViewController.view subviews]) {
if ([view isKindOfClass:[UINavigationBar class]]) {
NSLog(#"Found Nav Bar!!!");
myNavBar = (UINavigationBar *)view;
}
}
// Get the back button view
UIView *backButtonView = nil;
for (UIView *view in [myNavBar subviews]) {
if ([[[view class] description] isEqualToString:#"UINavigationItemButtonView"]) {
backButtonView = view;
NSLog(#"Found It: %#", backButtonView);
NSLog(#"Back Button View Frame: %f, %f; %f, %f", backButtonView.frame.origin.x, backButtonView.frame.origin.y, backButtonView.frame.size.width, backButtonView.frame.size.height);
}
}
CGPoint longPressPoint = [sender locationInView:myNavBar];
NSLog(#"Touch is in back button: %#", CGRectContainsPoint(backButtonView.frame, longPressPoint) ? #"YES" : #"NO");
if (CGRectContainsPoint(backButtonView.frame, longPressPoint)) {
// Place your action here
}
// Do nothing if outside the back button frame
}
I crawled all the web, but nothing.
I have a for loop to create uiimageview inside a uiscrollview controller.
It's a collection of images that you can scroll horizontally only.
for (int i = 0; i < NIMAGES; i++) {
NSString *filename = [NSString stringWithFormat:#"image%d.png", i+1];
ScrollerImage *iv = [[ScrollerImage alloc] initWithNibName:#"ScrollerImage" bundle:nil];
[iv initWithImage:[UIImage imageNamed:filename]];
iv.frame = CGRectMake(i * 320.0f, 0.0f, 320.0f, 150.0f);
iv.exclusiveTouch = YES;
iv.userInteractionEnabled = YES;
[sv addSubview:iv];
[iv release];
}
The ScrollerImage class is an UIImageView controller
#import <UIKit/UIKit.h>
#interface ScrollerImage : UIImageView {
}
#end
Now, how can I drag (not necessary a move, also a copy of view, or an id of view, anything that permit me to identify the image) an image from the scroller to another view?
It's like a shopping cart, where you can drag items at the bottom view (cart).
Like this:
This is an alternative, using UIGraphicsGetImageFromCurrentImageContext()
You can grab an image from currentScreen on click or after some events. Next renderize it to screen and apply a Pan gesture to move around the screen.
UIView *_DraggableView;
UIImageView *_imgvcChild1;
sample code:
- ( void ) onScrollviewClickOrOtherEvent
{
UIGraphicsBeginImageContext ( _DraggableView.frame.size );
[ _DraggableView.layer renderInContext:UIGraphicsGetCurrentContext() ];
UIImage *grab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if ( _imgvcChild1 ) [ _imgvcChild1 removeFromSuperview ];
_imgvcChild1 = [[ UIImageView alloc ] initWithImage:grab ];
_imgvcChild1.frame = _DraggableView.frame;
_imgvcChild1.userInteractionEnabled = YES;
[ self.view addSubview:_imgvcChild1 ];
UIPanGestureRecognizer *pan = [[ UIPanGestureRecognizer alloc ] initWithTarget:self action:#selector(moveObject:) ];
[ pan setMinimumNumberOfTouches:1 ];
[ _imgvcChild1 addGestureRecognizer:pan ];
}
- (void) moveObject:(UIPanGestureRecognizer *)pan
{
_imgvcChild1.center = [ pan locationInView:_imgvcChild1.superview ];
}
--- EDIT ---
A sample project on github: https://github.com/elpsk/Shopping-Cart
I've been trying to implement this in an app of mine and I had to just give up. I subclassed UIImageview like you have as well(which is a good option) and all my images are on a UIView that is a subclass of UIScrollView. You can pass your viewcontroller to the images themselves and change which view the imageview is a subview to when it reaches a certain boundary (I overrided the touchesMoved on the ImageViews themselves). The problem with this is it doesn't drag continously. The user has to literally select the image again when they dragged the image across a certain point because you're literally switching the uiimageview from one view to another.
I hope this gives you some insight at least, I really haven't found a solution to this issue. You'd think a lot of IPad apps would be paving the way to drag images all over different views and more tutorials appear on the issue!