CGRectIntersectsRect with 2 UIImageViews does not work - ios

I have a problem.
Briefly: CGRectIntersectsRect(object1.frame, object2.frame) placed in UIViewController does not work for object1 and object2 created in different classes (Class1 and Class2). I only can change their coordinates like object1.center.x, and object1.frame.size.width = 0 (I guess this is why CGRectIntersection function does not work). These objects just came through each other.
As I understand it may be connected with protocol/delegating, but I haven't found any sufficient information how to be in my situation.
Platform.m (my class for creating a platform in the ViewController)
#implementation Platform
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
platformView = [[UIImageView alloc]initWithFrame:frame];
platformView.image = [UIImage imageNamed:#"platform.png"];
[self addSubview:platformView];
}
return self;
}
ViewController.m (in the #interface there are Platform *platform; Kelvin *kelvin;)
-(void)addPlatform
{
platform = [[Platform alloc]initWithFrame:CGRectMake(initialPlatformX, (500 - arc4random()%200), 200, 10)];
[self.view addSubview:platform];
}
**if (!CGRectIntersectsRect(kelvin.frame, platform.frame)) {
NSLog(#"%f", platform.frame.size.width);
}**

Your explanation is confusing and hard to follow.
This sentence, for example, is a total mystery to me:
As I understand it may be connected with protocol/delegating, but i
haven't found any sufficient information how to be in my situation.
Delegation? Huh? Rectangles are not objects. Delegation is a design pattern for objects. How is that relevant here? (Answer: Very likely it isn't relevant at all.)
Moving on, though, here are a few things to consider:
2 views' frames will only use the same coordinate system if they are both subviews of the same view. So are your platform and your "kelvin" view subviews of the same view (It looks like you add platform directly to the view controller's content view, so is your "kelvin" view also a subview of the view controller's content view?)
Also, you make reference to object1.frame.size.width = 0
The CGRectIntersectsRect function checks to see if any pixels of the 2 rectangles overlap. If either of your rectangles has either a height or a width of 0, it's empty, and can't intersect with anything.

In initWithFrame, you are adding a UIImageView subview to Platform using the frame of Platform. That is incorrect. For example, if you add PlatForm at CGRectMake(10, 10, 100, 100), you'll then be adding the image view at CGRectMake(10, 10, 100, 100) within Platform (which is CGRectMake(20, 20, 100, 100) within the original view).
So, instead, I think you want to make a CGRect that starts at 0, 0 (or use the bounds of Platform, not its frame):
#implementation Platform
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
platformView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
platformView.image = [UIImage imageNamed:#"platform.png"];
[self addSubview:platformView];
}
return self;
}
By the way, just to be safe, you might also want to set clipsToBounds to YES, so that if you ever change contentMode to something that doesn't scale to fit, you don't have to worry about the image bleeding over its bounds.

Related

Objective-C: add subview only work for one view

For each UIImageView, I want to add the label subview to it.
Here is my class inherited form UIImageView
-(instancetype)initWithFrame:(CGRect)frame
{
if (self=[super initWithFrame:frame]) {
self.categoryLabel=[[UILabel alloc]initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 50)];
self.categoryLabel.textAlignment=NSTextAlignmentCenter;
self.categoryLabel.font=[UIFont systemFontOfSize:20];
self.categoryLabel.textColor=[UIColor whiteColor];
[self addSubview:self.categoryLabel];
NSLog(#"%#",self.subviews);
}
return self;
}
-(void)setModel:(HorizontalModel *)model
{
_model=model;
self.categoryLabel.text=self.model.category;
[self sd_setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"XXXXX%#",self.model.imgURL]] placeholderImage:[UIImage imageNamed:#"obama"]];
}
Here is my code in the view controller.
-(void)addImage:(NSNotification *)notification
{
self.HArrayLists=notification.userInfo[#"array"];
for (int i=0; i<[self.HArrayLists count]; i++) {
JTImageView *imageView=[[JTImageView alloc] initWithFrame:CGRectMake(i*310, 0, 300, 200)];
imageView.model=[HorizontalModel restaurantsDetailWithDict: self.HArrayLists[i]];
[self.mediaScrollView addSubview:imageView];
}
self.mediaScrollView.contentSize=CGSizeMake(310*[self.HArrayLists count], 0);
}
It turns out that only the first imageView shows a label, while the rest of the imageViews show only images.
I think the core of your problem is the line:
self.categoryLabel=[[UILabel alloc]initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 50)];
You are offsetting the x and y positions of the label by the x and y values of the image. This will place them outside the area of the image and with the image clipping, make them invisible. I think the line should be
self.categoryLabel=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, 50)];
to place all the labels at the top left corner of each image.
Having said that there are also a number of recommendations I would like to offer.
Firstly make all variable names start with a lowercase. So self.HArrayLists should be self.hArrayLists.
Secondly try and make variable names match their contents. So again looking at self.HArrayLists, perhaps something like self.imageData.
Next I would have done the composition differently. I would have a UIView to which I add both the UILabel and UIImageView instances. Using a parent view like this to layout two sub views often makes life easier.
I would also look into using a UICollectionView and UICollectionViewController rather than a UIScrollView. It will take you a bit of work to get your heads around how collection views work. But you will gain in terms of performance and better layout management.
Finally, study up on constraints. They're an essential part of building modern apps that can easily adapt to different sized screens, rotation and layouts.
You need to set as categoryLabel's frame properly.
self.categoryLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, frame.origin.y, frame.size.width, 50)];

should adding a UILabel in a custom UIView be in drawRect or initWithFrame / custom setup method

I'm not a full-time iOS dev and have to make some changes to someone else's code. We have a custom view where a UILabel is added in drawRect like this (edited for brevity):
- (void)drawRect:(CGRect)rect
{
UILabel *myLabel=[[UILabel alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 100.0f, 30.0f)];
myLabel.text=#"here is some text";
[self addSubview:myLabel];
}
I have never really seen this and thought that drawRect was ONLY for adding drawing operations (and have only seen UIBezierPaths). Should this be moved to initWithFrame (or a common setup method like setupMyView). Or is ok to leave in drawRect? Is there anything besides custom drawing that should be in drawRect?
Sorry for asking a somewhat basic question but even reading the Apple docs leave a bit to be desired.
Unless there is a very good reason to setup the view's subviews from within the drawRect method (I can think of none) I would strongly suggest leaving drawRect as purely a drawing method and move that addSubview stuff out of there! I would suggest overriding the -init method that is currently used to initiate the parent view. For example:
-(instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:(CGRect)frame]) {
UILabel *myLabel=[[UILabel alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 100.0f, 30.0f)];
myLabel.text=#"here is some text";
[self addSubview:myLabel];
}
return self;
}
I would definitely not do anything like that in drawRect. One method I've used in the past is to add it to layoutSubviews, because at that point the view is aware of the true bounds/frame. You'll want to ensure that you only generate the view stuff once, as layoutSubviews is called many times, such as on rotation. I usually do something like the following, where _viewGenerated is an instance variable:
- (void)layoutSubviews {
[super layoutSubviews];
if (!_viewGenerated) {
[self generateView];
_viewGenerated = YES;
}
}
- (void)generateView {
// do everything with any labels, images, etc., here...
}

Table that wouldn't appear in viewDidLoad

I have a static tableview with three rows I am creating programatically. I was creating it (incorrectly) in ViewDidAppear
CGRect fr = CGRectMake(10, 100, 280, 150);
SetUpTableView = [[UITableView alloc] initWithFrame:fr style:UITableViewStylePlain];
SetUpTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
SetUpTableView.delegate = self;
SetUpTableView.dataSource = self;
[self.view addSubview:SetUpTableView];
it was working fine.
I realized it was in the wrong location so i moved it to viewDidLoad
The table would NOT appear.
I commented out the auto resizing
CGRect fr = CGRectMake(10, 100, 280, 150);
SetUpTableView = [[UITableView alloc] initWithFrame:fr style:UITableViewStylePlain];
// SetUpTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
SetUpTableView.delegate = self;
SetUpTableView.dataSource = self;
[self.view addSubview:SetUpTableView];
and it now works fine in viewDidLoad.
I understand that I was in the wrong spot AND that this is a static table that doesn't need autoresizing but why would it work in viewDidAppear but not work in viewDidLoad?
It's all about timing.
viewWillAppear is called after the view hierarchy has finished being laid out. This means that whatever voodoo the auto resizing caused will be overridden by layout-related operations performed here.
viewDidLoad is called before the view has finished being laid out - this means that auto-resizing will occur after the code in viewDidLoad is executed.
Hope this clarifies things
In your viewDidLoad, how big is your view?
You have got magic numbers in your code - CGRectMake(10, 100, 280, 150);. If you want the tableview to be a fixed size from the left top right and bottom of your view, work it out, don't assume that you know the size of the view already!
Something like :
CGSize container = self.view.frame.size;
CGRect fr = CGRectMake(10, 100, container.width-60, container.height-100);
Otherwise, you might place your tableview outside the view and the autoresizing mask is just confused!

UIView: layoutSubviews vs initWithFrame

When subclassing UIView, I usually place all my initialisation and layout code in its init method. But I'm told that the layout code should be done by overriding layoutSuviews. There's a post on SO that explains when each method gets called, but I'd like to know how to use them in practice.
I currently put all my code in the init method, like this:
MyLongView.m
- (id)initWithHorizontalPlates:(int)theNumberOfPlates
{
self = [super initWithFrame:CGRectMake(0, 0, 768, 1024)];
if (self) {
// Initialization code
_numberOfPlates = theNumberOfPlates;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.frame];
[scrollView setContentSize:CGSizeMake(self.bounds.size.width* _numberOfPlates, self.bounds.size.height)];
[self addSubview:scrollView];
for(int i = 0; i < _numberOfPlates; i++){
UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:#"a1greatnorth_normal_%d.jpg", i+1]];
UIImageView *plateImage = [[UIImageView alloc] initWithImage:img];
[scrollView addSubview:plateImage];
plateImage.center = CGPointMake((plateImage.bounds.size.width/2) + plateImage.bounds.size.width*i, plateImage.bounds.size.height/2);
}
}
return self;
}
It's the usual tasks: setting up the view's frame, initialising an ivar, setting up a scrollview, initialising UIImages, placing them in UIImageViews, laying them out.
My question is: which of these should be done in init, and which of these should be done in layoutSubviews?
Your init should create all the objects, with the required data. Any frame you pass to them in init should ideally be their starting positions.
Then, within layoutSubviews:, you change the frames of all your elements to place them where they should go. No alloc'ing or init'ing should take place in layoutSubviews:, only the changing of their positions, sizes etc...
In case you're autoresizing works perfectly with just autoresizingFlags, or autolayout, you may just use init to setup the whole view.
But in general your should do layouting in layoutSubviews, since this will be called on every change of the views frame and in other situation, where layout is needed again. Sometimes you just don't know the final frame of a view within init, so you need to be flexible as mentioned, or use layoutSubviews, since you do the layout there after the final size has been set.
As mentioned by WDUK, all initialization code / object creation should be in your init method or anywhere, but not in layoutSubviews.

Shadow in separate view from UIImage for dynamic adjustments

I would like to obtain this effect (http://stackoverflow.com/questions/7023271/how-to-adjust-drop-shadow-dynamically-during-an-uiimageview-rotation) but from a more complex image than just a red square ! If the link ever gets broken, it's a about how to adjust drop shadow dynamically during an UIImageView rotation.
So I tried implementing something but I just can't get the shadow in a separate layer... Here is my code, very simple, but doesn't work:
// here is my code
- (void)viewDidLoad {
[super viewDidLoad];
testView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:#"handNoShadow.png"]];
testViewShadow = [[UIView alloc] initWithFrame:testView.frame];
testViewShadow.layer.shadowPath = [[testView layer] shadowPath];
testViewShadow.layer.shadowOpacity = 1.0;
testViewShadow.layer.shadowOffset = CGSizeMake(10, 10);
testViewShadow.layer.shadowRadius = 5.0;
[self.view addSubview:testViewShadow];
[self.view addSubview:testView];
}
PS: i did #import
I do get an image but no shadow... =(
Any lead, help, code, link... is welcome !
Thanks
possible cause:
your testViewShadow.clipToBounds property is set to YES (should be NO)
your testViewShadow do the drawing of the shadow correctly but another UIView is on top and mask it. Check your Z order. Either the order in Storyboard/Nib file (or the order you added the subviews programmatically). Last in the list (or last one added) is on top. For my app I had to put the UIView that need a shadow last so that no other view mask it.

Resources