Adding SKScene to a UICollectionViewCell lowers scrolling performance - ios

I am trying to add a SKScene with a simple animation to a UICollectionCell.
I've got my SKView setup in the .xib file of the collection view cell.
If I run it and scroll, it works as expected. But if I change to a different view controller (in a tab bar controller) that also uses those types of cells and start scrolling, it will get stuck.
Here's how I am adding the SKScene:
#interface MAPostCollectionViewCell ()
#property (strong, nonatomic) IBOutlet SKView *viewAnimation;
#property (strong, nonatomic) MAHeartAnimation *heartScene;
#end
#implementation MAPostCollectionViewCell
-(void)layoutSubviews{
[super layoutSubviews];
self.heartScene = [MAHeartAnimation sceneWithSize:self.viewAnimation.bounds.size];
self.heartScene.scaleMode = SKSceneScaleModeAspectFill;
[self.viewAnimation presentScene:self.heartScene];
}
#end
And I only trigger the animation if the user taps a button.
But for the scope of this question, the animation doesn't really matter, as I am not touching that button, just scrolling the UICollectionView.

It turns out you should not use SpriteKit and UIKit this way. Even though they are modular frameworks, they are not performant when using together this way. I ended up using UIKit for the animations that I wanted, by using CALayers.

Related

UIView custom xib doesn't show up

I'm having a problem that doesn't seem to have an obvious solution. I've searched around and I've been through all common answers that I could find.
My custom xib views don't show up on the app when I launch. The background is clear, and this xib has 5 image views as you can see below which aren't set to hidden.
The class has about 5 delegates as well which I set from a caller when the delegates have been initialized. The caller of initWithDelegates: is the parent UIViewController that displays this xib.
CustomView.h
#interface CustomView : UIView<SomeProtocol>
// UI Items - THESE AREN'T SHOWING UP
#property (strong, nonatomic) IBOutlet UIImageView *image1;
#property (strong, nonatomic) IBOutlet UIImageView *image2;
#property (strong, nonatomic) IBOutlet UIImageView *image3;
#property (strong, nonatomic) IBOutlet UIImageView *image4;
#property (strong, nonatomic) IBOutlet UILabel *label;
#property (strong, nonatomic) IBOutlet UIStackView *centerStackView;
- (id)initWithDelegates:(UIViewController*)delegate1 withDelegate2:(NSObject<Delegate2>*)delegate2 withDelegate3:(NSObject<Delegate3>*)delegate3 withDelegate4:(UIViewController<Delegate4>*)delegate4
withDelegate5:(NSObject<Delegate5>*)delegate5;
#end
CustomView.m
#implementation CustomView
- (void)awakeFromNib {
[super awakeFromNib];
}
- (id)initWithDelegates:(UIViewController*)delegate1 withDelegate2:(NSObject<Delegate2>*)delegate2 withDelegate3:(NSObject<Delegate3>*)delegate3 withDelegate4:(UIViewController<Delegate4>*)delegate4
withDelegate5:(NSObject<Delegate5>*)delegate5
{
NSArray * arr =[[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:nil options:nil];
self = [arr firstObject];
self.delegate1 = delegate1;
self.delegate2 = delegate2;
self.delegate3 = delegate3;
self.delegate4 = delegate4;
self.delegate5 = delegate5;
[self setLoopImage];
[self layoutIfNeeded];
return self;
}
#end
What else I've verified:
I made sure that the xib file is properly selected under "Custom Class" in the interface builder.
I've walked through the code to make sure there are no crashes or obvious issues.
The xib doesn't appear to have any obvious issues in interface builder such as being set to HIDDEN.
The 5 IBOutlet ImageViews get a memory address and aren't nil.
Any help is appreciated!
UPDATE:
I believe this has something to do with putting the nibs in a nested UIView. In the interface builder, the order is UIViewController -> UIView -> nibs
There doesn't seem to be a method to register nibs from the parent UIViewController as there are in classes like UITableView.
I've also tried:
Setting the background color in the xib to make sure it shows up. It doesn't. However if I set the background color to red in the parent where I added the view in interface builder, it turns red. (See Figure 1), and my xib file is shown in Figure 2
Over-rode initWithFrame to make sure its being called. It is called.
Thinking that the view may be instantiated twice, one via autolayout and one via my custom init method, I tried making the (id)initWithDelegates method not init or register the nib in case that was duplicating the object. I did this by removing 1) return self, 2) the lines that register the nib, and 3) making the method prototype return void - I.E. (void)initWithDelegates - This didn't work either.
I tried these lines within initWithDelegates :
[self addSubview: self.image1];
[self bringSubviewToFront: self.image1];
[self.menuImage setHidden:image1];
-- with no luck.
Figure 1: (Actual result)
Figure 2 (Expected.. with red background)
Check your Autolayout in your custom XIB. Something the contain does not show because of it. For example: only center vertically & center horizontally. Try to do different autolayout constraint in your view in custom class.
I think something is wrong with layout of your CustomView inside ParentView. Could you please:
Check the frame of CustomView when you add it into the ParentView.
Check constraints that has CustomView inside ParentView. If you don't
have them - add it, constraints will iOS the glue how to layout your
CustomView. More information about this you can find here Programmatically Creating Constraints
And also you can try to use "Debug View Hierarchy" in Xcode to
inspect what is going on on your screen, there you can find your
CustomView in the view hierarchy and you will see what is wrong.
nibs cannot be nested within the structure.
Instead of:
UIViewController -> UIView -> nibs..
It should be:
UIViewController -> Container View -> UIView with nibs.

Adding tag after switching view controllers

I have a randomly generated int that I assign to a tag for a UIImageView on my storyboard in ViewWillAppear.
When I segue to the main menu and try to enter the view again, however, the app crashes. I know it's because of the tag because when I remove it everything works fine.
Any thoughts on why it works the first time but not on times after that? Code below.
ViewController.h:
#interface ViewController : UIViewController{
int tagnumber;
IBOutlet UIImageView *box;
...
}
ViewController.m:
-(void)viewWillAppear:(BOOL)animated{
tagnumber = arc4random()%1000;
box.tag = tagnumber;
...
}
- (IBAction)unwindToThisViewController:(UIStoryboardSegue *)unwindSegue
{
[_animator removeAllBehaviors];
[box removeFromSuperview];
}
MainMenu.m:
-(IBAction)prepareForUnwind:(UIStoryboardSegue *)segue {
}
Basically, when the view disappears and the system is running out of memory, it will call on UIViewController's implementation of didReceiveMemoryWarning. There is a fantastic description of this here: ViewWillDisappear versus dealloc
When you are creating an IBOutput you should really have a weak pointer to it by saying #property (weak, nonatomic) IBOutlet UIImageView *box. Views retain their subviews, and therefore, if a view gets deallocated, its subviews retain count decreases by one. When the view disappears, the system will decide when to deallocate self.view, and when it does, it will automatically release box, and box will get deallocated as well. You don't really need to worry about it.

how to make text appear on multiple views with one label?

I'm using a scroll view with 3 views, and I need to place one label that shows up on each view,
the views are on separate view controllers: in view controller.h
#import "PagerViewController.h"
#interface ViewController : PagerViewController {
}
#property (strong, nonatomic) IBOutlet UIView *View1;
#property (strong, nonatomic) IBOutlet UIView *View2;
#property (strong, nonatomic) IBOutlet UIView *View3;
I've placed them as (in ViewController.m)
[self addChildViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"View1"]];
Should I fix an NSString
and Outlet to do this?
I need the labels to move with the scroll not to be separate from each other.
What you want, cannot be done with a single UILabel. You will have to create 3 separate labels and write the logic that will update all of them.
Create the UILabel outlets and write a method something similar to the method below:
- (void)updateLabels:(NSString *)text
{
self.label1.text = text;
self.label2.text = text;
self.label3.text = text;
}
This is just the way UIView's work and you cannot make them draw outside of their bounds.
I suggest you follow up on more iOS basics before going into complex layouts.
If you need to separate these views, a well-maintainable solution could be three separated label and one function (updateLabels:) in which the three label is updated to the same value.

Drawing with Drawrect

dear objectiveC masters,
I'm a newbie apple programmer and since I'm currently taking a master degree in math education, I want the make a thesis on making apps that based on math education..
Right now, I'm trying to make an iPad app that draws a sine function and then transforms the sine function. I draw the sine graph by overriding the drawrect function in a custom uiview class and load it to a uiview object. The sine function is drawn nicely on a context, along with grids and axes that are drawn on different contexts.
I put several sliders and then plan to use the sliders to change the variables in the uiview class that i use for drawing. Now here's the problem, I realize that I can't access the variables in the viewcontroller from the custom uiview class, and I suspect that I may have mistakenly used the wrong paradigm in writing the whole program.
Can someone help me to clear the confusion here? It doesn't have to be in the exact codes but more to the big pictures of how I should draw and redraw the sine function on a view object whilst changing the variables of the sine function through sliders.
Thank you for your kind help :)
Chandra from Indonesia.
There are two ways to solve this:
Instead of letting the UIView ask the values from the UIViewController, you can also push the values to the UIVIew the moment one of the sliders changes. This way the UIView does what it should do: draw what the ViewController asks him to.
Think of a function like redrawUsingNewValues: that you implement in the UIView and you call from the UIViewController.
Use delegation. If you really want the UIView to be in control, you can give it a pointer to the UIViewController using delegation. That way the UIView doesn't own the UIViewController, but you can get the values you want.
An introduction on delegation can be found here: Delegation and the Cocoa Frameworks
Good luck on your program!
A method in the UIViewController should recognize when the slider values changes
the same method should trigger another methode within the UIViewController to update/recalculate the sine function values (e.g. creating an value array)
at the end of the update-methode the necessary values are transferred to the UIView via an outlet from UIViewController to the UIView (the UIView is an property of the UIViewController)
the UIView is drawing the new sine-function in draw rect
Edit 1:
your ViewController.h:
#import <UIKit/UIKit.h>
#class YourGraphUIView; // that's you view where you draw
#interface ResultViewController: UIViewController
#property (weak, nonatomic) IBOutlet UISlider *valueFromSlider; //bound to your UISlider
#property (weak) IBOutlet YourGraphUIView *yourGraphUIView; //bound to your costumUIView
#property (nonatomic, retain) NSNumber *graphValue;
- (IBAction)takeSliderValue:(id)sender; //bound to your UISlider
#end
your ViewController.m:
#import "ResultViewController.h"
#import "YourGraphUIView.h"
#interface ResultViewController ()
#end
#implementation ResultViewController
#synthesize yourGraphUIView, valueFromSlider, graphValue;
- (IBAction)takeSliderValue:(UISlider *)sender{
graphValue = [NSNumber numberWithDouble:[(double)sender.value]]; //takes value from UISlider
yourGraphUIView.graphValue = graphValue; //gives the value to the yourGraphUIView
[self.yourGraphUIView setNeedsDisplay] //<--- important to redraw UIView after changes
}
end
your YourGraphUIView.h:
#import <UIKit/UIKit.h>
#interface YourGraphUIView : UIView
#property(nonatomic, retain)NSNumber *graphValue;
- (void)drawRect:(CGRect)dirtyRect;
#end
your YourGraphUIView.m:
#import "YourGraphUIView.h"
#implementation YoutGraphUIView
#synthesize graphValue;
//... init, draw rect with using the graphValue for calculating and updating the graph
end;
I hope this helped. You should have a look how to build the GUI and how to connect the UIViews. You need to set the custom classes for the ViewController and the YourGraphUIView as well. Good luck!

Resizing a button in iOS programmatically

I am using XCode 4.6.1 and developing for iOS 6.
I have added a button to the storyboard. I have created an outlet in my implementation file ViewController.m:
//
// ViewController.m
//
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIButton *b1;
#end
I try to change the button b1's properties as follows (in this same file: ViewController.m):
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.b1.alpha = 0.5;
self.b1.frame = CGRectMake(0,0,123,412);
}
When I run the app in the simulator, the alpha of the button is successfully set to 0.5.
However, the position and size of the button doesn't change.
I have tried various ways to make it happen. However nothing seems to work. I would like to know what am I doing wrong. I am pretty new to Objective C. Any help would be greatly appreciated.
You are probably using autolayout:
When using autolayout you cannot manually change your outlets frames. You have two options: you can either disable autolayout (then your code should work), or create outlets to your constraints and then modify the constrains programmatically:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *widthConstraint;
and then in your code:
self.widthConstraint.constant = 123;
and so on with all the constraints that need to be modified.

Resources