NSImageView disappears without reason - objective-c

I let my window autoresize to the visible screen. There is no problem in that code.
But the problem lies here I need to add ImageViews to the window and of course they should also be able to resize.
Code to create the ImageViews and to make window fit to current visible
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
int x,y;
NSInteger rowCounter,gapX,gapY;
rowNumber=5;
rowCounter=0;
gapX=0;
gapY=0;
personNumber=10;
startX=20;
startY=20;
NSRect rect;
NSSize size;
NSScreen *scren=[NSScreen mainScreen];
rect=scren.visibleFrame;
y=rect.size.height;
x=rect.size.width;
NSLog(#"%s%s%d%s%d","screen"," y size=",y," x size=",x);
y=window.frame.size.height;
x=window.frame.size.width;
NSLog(#"%s%s%d%s%d"," window"," y size=",y," x size=",x);
timer1 =[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(resizeWindow1) userInfo:nil repeats:YES];
rahmen=[[NSMutableArray alloc] initWithCapacity:25];
NSPoint p;
y=rect.size.height;
x=rect.size.width;
size.height=y;
size.width=x;
p.x= rect.origin.x;
p.y= rect.origin.y+y;
[window setContentSize:size];
[window setFrameTopLeftPoint:p];
width =x-(20*(personNumber/2));
width=(width/(personNumber/2))-(20/(personNumber/2));
NSLog(#"%s%ld","\n width: ",(long)width);
high= y-(20*rowNumber);
high=high/rowNumber-(20/rowNumber);
NSLog(#"%s%ld","\n high: ",(long)high);
for (indexRahmen=0; indexRahmen<25; ) {
if (indexRahmen>0 && indexRahmen%(personNumber/2)==0) {
rowCounter=rowCounter+1;
gapX=0;
gapY=gapY+1;
}
else if (indexRahmen%(personNumber/2)!=0){
gapX=gapX+1;
}
if (rowCounter==rowNumber) {
rowCounterr=rowNumber-1;
gapY=gapY-1;
}
rect=NSMakeRect(startX+(width*(indexRahmen%(personNumber/2)))+(gapX*20),(startY+(rowCounter*high)+(gapY*20)),width, high);
NSImageView *imageView=[[NSImageView alloc] initWithFrame:rect];
imageView.image = [NSImage imageNamed:#"umrandung.png"];
[rahmen insertObject:imageView atIndex:indexRahmen];
[window1view.self addSubview:imageView];
NSLog(#"%s%ld","\n IndexZahl: ",(long)indexRahmen);
indexRahmen=indexRahmen+1;
}
}
the resizing action called by timer1 is nearly the same except that:
NSImageView *imageView= [rahmen objectAtIndex:indexRahmen];
imageView.frame= rect;
imageView.image = [NSImage imageNamed:#"umrandung.png"];
[rahmen insertObject:imageView atIndex:indexRahmen];
and that imageView is not added to subview of window1view
and the strange thing that does happen is that the 1. NSImageView disappears but when I check the coordinates they are on the correct spot so I don't know why it disappears.

I found out myself how to fix it.
I used instead of creating the ImageView with:
[rahmen objectAtIndex:indexRahmen];
I used:
for (NSImageView* ImageView in rahmen)
and It works fine with that.
Sadly I don't know the reason why it disappeared with the other method

Related

How to Scroll the text in label automatically

I have an array , data of which 'm displaying on scrollview using UILabel. I can scroll right and left. But I want to add one more feature in that which is automatic scrolling of the label text on scrollview. That means even if I do not scroll I want it to move by itself.
My code for scrolling is as below:
- (void)viewDidLoad
{
[super viewDidLoad];
_scrollView.contentInset=UIEdgeInsetsMake(0, 300, 0, 300);
NSMutableArray *items=[[NSMutableArray alloc] initWithObjects:#"A walk to remember",#"The Last song",#"The Notebook",#"Dear John",#"The Longest Ride",nil];
CGFloat y = 10;
self.label = [[UILabel alloc]initWithFrame:CGRectMake(0,y,800, 25)];
for(int i=0;i<[items count];i++){
y+=20;
NSString* lab=[items objectAtIndex:i];
_label.backgroundColor=[UIColor clearColor];
lab=[items componentsJoinedByString:#" | "];
NSLog(#"label:%#",lab);
[self.label setLineBreakMode:NSLineBreakByWordWrapping];
[self.label setText:lab];
}
NSTimer *timer = [NSTimer timerWithTimeInterval:1
target:self
selector:#selector(timer)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//[_scrollView scrollRectToVisible:NSMakeRange(0,0,0.0) animated:YES];
[_scrollView addSubview:_label];
[UIView commitAnimations];
_scrollView.contentSize=CGSizeMake(_scrollView.frame.size.width*[items count], _scrollView.frame.size.height);
}
timer code:
-(void)timer {
[self.label setText:[NSString stringWithFormat:#" %#", self.label.text]];
}
Please help me how could I make the label text move horizontally. Thanks for any help
That's a fairly horrible way to implement scrolling if I may so so! You're scrolling by inserting incremental spaces to the left of the string. There is a much easier way to do this!
Keep your code that adds the UILabels to your scroll view
Get rid of the NSTimer stuff
Add a basic UIView animation to animate the scrollview's content offset.
Something like this:
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
_scrollview.contentOffset = CGPointMake(-50.0f, 0.0f);
} completion:nil];
This will slide the whole scrollview left by 50 points over 0.3secs. You can play around with the settings.
btw, typically you'd create the UILabels in viewDidLoad (as you have it) but put the animation in viewWillAppear or viewDidAppear
Have had another look at your code. I thought you were adding multiple UILabels to your uiscrollview but actually you're adding just one with a long text string.
You can simplify your code to set up the label quite a bit, then implement the scrolling animation per the below.
NSMutableArray *items=[[NSMutableArray alloc] initWithObjects:#"A walk to remember",#"The Last song",#"The Notebook",#"Dear John",#"The Longest Ride",nil];
CGFloat y = 10;
self.label = [[UILabel alloc]initWithFrame:CGRectMake(0,y,800, 25)];
[self.label setLineBreakMode:NSLineBreakByWordWrapping];
[self.label setBackgroundColor: [UIColor clearColor]];
[self.label setText: [items componentsJoinedByString:#" | "]];
EDIT
To your question about how to show the whole line scrolling and then repeating, there are a couple of steps here.
Make the UILabel wide enough to take all the text on a single line - i.e. increase the width so that it's big enough to fit all the text. You can do this by trial and error or use (CGSize)sizeThatFits:(CGSize)size
to get the size. You call CGSize size = self.label sizeThatFits:CGSizeMake(CGFloatMax, self.label.frame.size.height) to get the size then apply this to the label's frame:
CGRect frame = self.label.frame;
frame.size.width = size.width; //calculated above with sizeThatFits
self.label.frame = frame; //update the frame
Now scroll the scrollview by the full width. So the animateWithDuration code would become:
_scrollview.contentOffset = CGPointMake(-size.width, 0.0f);
The final step is how to make it restart once it's got to the end. Not sure what effect you want here? Should it jump back to the start? (if so set the contentOffset x to zero), or continue scrolling? (if so, use a trick like adding a second UILabel at the end of the first.
BTW, if what you're after is a simple scrolling text like a banner on a webpage, then I suggest you don't bother with the scrollview at all. Instead add the UILabel to self.view and then animate the UILabel's frame origin directly.

UITapGesture only works after reLoad UIViewController

I have 1 year works with Objective-C and is my first question that I make because I always found the answer in this site, but this situation is completely insane and did not found anything similar.
I have a custom class (called BallClass from UIView). The UIView have an UIImageView inside on it and a UITapGesture to make an action.
So when I create the object in a UIViewController, does not works the tapAction when press in the object, but if I change to other screen and return it, the tapGesture works perfect.
I tried many options, but I cant makes work in the first load.
I work with ARC, Xcode (4.6.3) and OSX (10.8.4) and IB but this objects was created with no IB .
Thanks in advance.
Here is the class BallClass.m
- (void)tapGesture:(UIGestureRecognizer *)tapGesture {
NSLog(#"hola");
}
- (id)initBall:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
// Recibir las variables iniciales para el control del timer
ball = [[Ball alloc] init];
// Agregar la imagen de la pelota en el view
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height)];
[imageView setCenter:CGPointMake(CGRectGetMidX([self bounds]), CGRectGetMidY([self bounds]))];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[imageView setImage:[UIImage imageNamed:#"pelota.png"]];
[imageView setTag:100];
[imageView setNeedsDisplay];
// Add TAP
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesture:)];
// Tap Properties
[tapGesture setDelaysTouchesBegan:YES];
[tapGesture setNumberOfTapsRequired:1];
[tapGesture setDelegate:(id)self];
[imageView addGestureRecognizer:tapGesture];
[self addSubview:imageView];
[self setNeedsDisplay];
// Propiedades del View
[self setUserInteractionEnabled:YES];
self.backgroundColor = [UIColor clearColor];
[self setTag:100];
// BALL BOUNCE
[NSTimer scheduledTimerWithTimeInterval:1.0 / 30.0 target:self selector:#selector(onTimer:) userInfo:nil repeats:YES];
}
return self;
}
- (void)onTimer:(NSTimer *)timerLocal {
// Do something
}
... // more methods
Here is the instantiate:
ballView00 = [[BallView alloc] initBall:CGRectMake(10, 10, 40, 40)];
The UiView show perfect, and the animation works fine, but the UITapGesture just works, as mentioned early, after reLoad the ViewController.
Here is the link to image the Ball and UIView (#kBall). Sorry, about image, but I cant load until I have 10 points :(
This might be an issue with the superview's frame. What view is holding ballView00? Does ballView lie entirely within it's superview's bounds?
Usually when you encounter problem where you're expecting a touch but don't get it it's because the view set to receive the touch is outside one of its parents' bounds.
So if the superview bounds are 100x100 and the ball is at x=125 y=125 it will not receive a touch.
A lot of magic happens when your views get laid out, it's not surprising to sometimes find one with 0x0 dimensions because of some incorrect constraints.
You can easily check the bounds of all your views by giving them a background color.
I found the solution.
The ball make a spin with this:
[UIView animateWithDuration: 0.5f
delay: 0.0f
options: UIViewAnimationOptionCurveEaseIn
animations: ^{
self.transform = CGAffineTransformRotate(self.transform, M_PI / 2);
}
completion: ^(BOOL finished) {
if (finished) {
if (animating) {
// if flag still set, keep spinning with constant speed
[self spinWithOptions: UIViewAnimationOptionCurveLinear];
}
}
}];
And generate conflict with the main timer. I change the spin action with simple add self.transform = CGAffineTransformRotate(self.transform, M_PI / 2); inside drawRect method in the class.
Apparently, I can't nested two timers, because "disable" the tapGesture, so when I change of the screen and return, the spin was stopped and this help to found this situation.
Thanks for your help.

Change the height of NavigationBar and UIBarButtonItem elements inside it in Cocoa Touch

I suppose it's not strictly in line with Apple guidelines but I guess it must be possible somehow. I'd like to change the height of navigation bar inside UINavigationController and the height of UIBarButtonItem elements inside that bar.
Using a trick from this question I managed to change the height of navigation bar but I can see no way of adjusting the height of bar button items.
If anyone knows how to change the size of bar button items, please help me out.
This is my solution. It works very well.
#interface UINavigationBar (CustomHeight)
#end
#implementation UINavigationBar (CustomHeight)
- (CGSize)sizeThatFits:(CGSize)size {
// Change navigation bar height. The height must be even, otherwise there will be a white line above the navigation bar.
CGSize newSize = CGSizeMake(self.frame.size.width, 40);
return newSize;
}
-(void)layoutSubviews {
[super layoutSubviews];
// Make items on navigation bar vertically centered.
int i = 0;
for (UIView *view in self.subviews) {
NSLog(#"%i. %#", i, [view description]);
i++;
if (i == 0)
continue;
float centerY = self.bounds.size.height / 2.0f;
CGPoint center = view.center;
center.y = centerY;
view.center = center;
}
}
Maybe this tutorial about a customized navbar will help you: Recreating the iBooks wood themed navigation bar
If you create a BarButtonItem with a UIImageView you can maybe change the framesize/boundsize of the custom UIImageView
UIImageView* imageView = [[[UIImageView alloc] initWithFrame:navigationController.navigationBar.frame] autorelease];
imageView.contentMode = UIViewContentModeLeft;
imageView.image = [UIImage imageNamed:#"NavBar-iPhone.png"];
[navigationController.navigationBar insertSubview:imageView atIndex:0];
So for your need you would give the -initWithFrame method appropriate values.
static CGFloat const CustomNavigationBarHeight = 74;
#implementation WTNavigationBar
- (CGSize)sizeThatFits:(CGSize)size{
size.width = 1024;
size.height = CustomNavigationBarHeight;
return size;
}
-(void)layoutSubviews {
[super layoutSubviews];
for (UIView *view in self.subviews) {
SFLog(#"view.class=%#",[view class]);
if ([view isKindOfClass:NSClassFromString(#"UINavigationItemButtonView")]) {
float centerY = self.bounds.size.height / 2.0f;
CGPoint center = view.center;
center.y = centerY;
view.center = center;
}
}
}
#end
in my iPad app,which has a fixed landscape orientation,I found I have to hardcode the size's width
I managed to do something similar by subclassing UINavigationBar and overriding -layoutSubviews. The code looks like:
-(void)layoutSubviews {
[super layoutSubviews];
int i = 0;
for (UIView *view in self.subviews) {
NSLog(#"%i. %#", i++, [view description]);
if ([view isKindOfClass:NSClassFromString(#"UINavigationButton")]) {
view.frame = CGRectMake(0, 0, 100, 50);
}
}
}
If you need to know how to subclass UINavigationBar, have a look at this very good answer.
I am not really sure about the NSClassFromString(#"UINavigationButton")] part. It works, but I did this as an experiment, and I'm not sure if this will get approved by Apple. I hope someone with a better knowledge might shed some light.
For the UINavigationbar
In iOS SDK 4.3 and beyond, there is a way (hack) to change the height of the UINavigationBar.
To change the height of UINavigationController, change its frame size in viewWillAppear:animated: function. Then, the height will stay customized throughout whole app.
For the UIBarButtonItems
I've actually run into this myself and the only thing I could come up with was leveraging initWithCustomView and passing in a UIButton with a defined frame.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
/*
* Insert button styling
*/
button.frame = CGRectMake(0, 0, width, height);
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
Otherwise UIBarButtonItem only has a width property that can be set but unfortunately not a height property. Another nifty thing I've done with initWithCustomView is to pass in a toolbar with a button and other things like activity indicators. Hope this helps.
How badly do you want this? And, how thin (or thick) do you want to make your navbar?
One approach would be to set the transform of the navbar to scale and translate it. If you scale it too much the title and button text will look wonky, but if you only need to shave a few pixels you might be allright.
Here's the result of scaling it to be 75% of full height (33 pixels tall):
And the code that produced this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = #"Thin Navigation Bar";
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle: #"Press Me" style:UIBarButtonItemStyleBordered target: nil action: NULL ] autorelease];
CGFloat scale = .75;
CGFloat cy = 44.0 - ( 44.0 * scale );
self.navigationController.navigationBar.transform = CGAffineTransformScale( CGAffineTransformMakeTranslation( 0, -cy / 2.0 ), 1.0, scale ) ;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGFloat scale = .75;
CGFloat cy = 44.0 - ( 44.0 * scale );
CGRect r = self.view.frame;
r.origin.y -= cy;
r.size.height += cy;
self.view.frame = r;
}
Now, this does have a number of problems, which may or may not be solvable. #1 is that you're fighting with the UINavigationController to size and position the navbar and the view-controller views. Animating between view controllers that use this technique is likely going to look weird.
I'd be curious if you could solve the related issues...
One last thought: If you dont use a UINavigationController then there really aren't a whole lot of issues with this other than squished text. Or, you could use a navigation controller but hide the default navbar, and add the thin navbar to each of your child-view controller views. You could even subclass UINavigationBar and set the transform from within:
#interface TSThinNavBar : UINavigationBar
{
}
#end
#implementation TSThinNavBar
// assuming we'll always be constructed from a nib
- (id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder: aDecoder];
if ( self != nil )
{
CGFloat scale = .75;
CGFloat cy = 44.0 - ( 44.0 * scale );
self.transform = CGAffineTransformScale( CGAffineTransformMakeTranslation( 0, -cy / 2.0 ), 1.0, scale ) ;
}
return self;
}
#end

Making fading effect with NSView

I have a subview which I want to show up to make a dissolve fade effect. For that purpose, I'm trying to:
Show it
Fade it
For that purpose, I set a "showFade" method on my superview (Fader is the subview defined previously. someImage is already defined).
- (void)showFade {
[Fader setHidden:NO];
[Fader setImage: someImage];
[[Fader animator] setHidden:YES];
}
The problem is: it works for the first time, and the subview fades, but it never shows up again. What am I doing wrong?
EDIT: As requested, here is a more complete sample of the code
- (void)awakeFromNib {
Fader = [[NSImageView alloc] initWithFrame: [self bounds]];
Images[0] = #"Tower";
Images[1] = #"Desert";
Images[2] = #"Fall";
image = 0;
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(showFader:) userInfo:nil repeats:YES];
[self addSubview: Fader];
[self nextImage];
[super awakeFromNib];
}
- (void)showFader: (NSTimer*)timer {
[Fader setImage: [self image]];
[Fader setHidden:NO];
[[Fader animator] setHidden:YES];
[self nextImage];
}
- (void)nextImage {
if (image == 2) {
image = 0;
}
else {
image++;
}
[self setImage: [NSImage imageNamed: Images[image]]];
}
So, basically, I have a repeating timer making the parent NSImageView to loop between an array of images and making the "Fader" to show up with the previous image and fade out. The problem is that it only shows up once.
I had the same problem a long time ago (yet I never solved it), but I think this could have something to do with the fact that you ask your view to be showed and dissolved in the same event loop.
I guess you should let the event loop make an iteration (so that your view is actually displayed) before asking to dissolve the view.
I guess you could do it by setting a (short) timer that would fire a couple of millisecs after the view is displayed (by setHidden:NO).
I also guess there are easier ways to solve the problem (and I would be glad to hear them).
What is more, you could also try animator setAlphaValue:0.0 to have a smooth transition.
I am not definitely not sure I have a correct answer, that's only a suggestion...
hidden is a BOOL property, you can't animate BOOLs cause there are only two discrete values: NO and YES. Use the opacity instead, you can animate opacity from 1.0 (fully visible) to 0.0 (invisible) to fade out. And hidden has to be set to NO for this to work, otherwise it will be hidden all the time.
This method works perfectly for me to make an image start out blurry and smoothly come into focus.
let parentView = UIView()
let imageView = UIImageView(image: UIImage(named: "ImageToFocus"))
parentView.addSubview(imageView)
let blurEffect = UIBlurEffect(style: .Light)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = imageView.frame
blurView.alpha = 1.0
self.blurTimer = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("animateBlur:"), userInfo: blurView, repeats: true)
func animateBlur(timer:NSTimer)
{
let blurView = timer.userInfo as UIVisualEffectView
if blurView.alpha > 0.0 {
blurView.alpha -= 0.05
} else {
blurTimer = nil
}
}

How to pass scrolling input to a different view

This site really is awesome.
I have what is hopefully a simple question this time. I would like to pass any scrolling input from the user (could be wheel, touchpad, etc) to an NSScrollView which contains my own subviews.
At the moment if the user scrolls just on the documentView (outside of my subviews' frames) the scroll works normally but if they scroll while the cursor is over a subview nothing happens. So basically I'd like to have the subview recognise the scroll event and pass it back to the scroll view.
Any help is greatly appreciated.
Cheers.
EDIT:
Here is the code I'm using to add the subviews to the documentView
_milestoneView and _activityView are both NSView subclasses which have a corresponding nib (created with instantiateNibWithOwner and objects hooked up accordingly) they contain a NSTextField, PXListView and some have a NSProgressIndicator.
-(void)useProject:(NSNumber *)projectId
{
[self resetView];
NSRect bounds = [[self view] bounds];
NSRect defaultFrame = NSMakeRect(20, NSMaxY(bounds)-93, NSMaxX(bounds)-20, 50);
//Prepare the milestone view
if (_milestoneView == nil)
_milestoneView = [MilestoneView milestoneViewFromNibWithFrame:defaultFrame withProject:[BCLocalFetch projectForId:projectId]];
[_milestoneView reloadList];
//Prepare the activity view
if (_activityView == nil)
_activityView = [ActivityView activityViewFromNibWithFrame:defaultFrame withProject:[BCLocalFetch projectForId:projectId]];
[self refresh];
}
I then use the refresh method to reposition them as the content sizes vary so I wanted to have a separate method.
-(void)refresh
{
//Position the milestones first
[_milestoneView setFrameOrigin:NSMakePoint(20, NSMaxY([[self view] bounds])-[_milestoneView frame].size.height-60)];
if ([[_milestoneView milestones] count] > 0)
[[self view] addSubview:_milestoneView];
//Now the activity view
[_activityView setFrameOrigin:NSMakePoint(20, [_milestoneView frame].origin.y-[_activityView frame].size.height-20)];
[[self view] addSubview:_activityView];
[self autosizeView];
}
-(void)autosizeView
{
//Resize the view to accommodate all subviews
NSRect oldFrame = [[self view] frame];
CGFloat lastY = [_activityView frame].origin.y;
if (lastY < 0) {
CGFloat newHeight = oldFrame.size.height + (-lastY);
[[self view] setFrameSize:NSMakeSize(oldFrame.size.width, newHeight)];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"BBContentDidResizeNotification" object:self];
}
Ok so I came back to the issue and finally got it worked out. The implementation was actually quite simple.
I added a property to PXListView to point to the NSScrollView that is to be scrolled.
I then implemented NSResponder's scrollWheel: method like this:
-(void)scrollWheel:(NSEvent *)theEvent
{
//Pass scrolling to the superview
[_scrollHandler scrollWheel:theEvent];
}
And all is well!