I have a very simple UIView, that is only drawing a triangle. It implements a UIView drawRect method that is drawing a figure. It is working fine on iOS7, but when I run the same code on iOS8 it is not even called. Any ideas what has changed on iOS8? My code in nutshell:
#interface MyView : UIView
#end
#implementation MyView
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
// draw things
}
#end
myView = [MyView new];
[self addSubview:myView];
Update
My view hierarchy: UIViewController View->Custom UILabel->MyView
It is an error message bubble, and my view is a beak pointing to something.
I have used a new UI debugging tool, but it is not visible there. Init method is called, drawRect is not. Something must have changed in iOS8, because iOS7 is calling drawRect.
Update 2
Of course I'm using constraints (and Masonry pod), and this is why I did not specify the frame.
[myView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(make.superview.mas_bottom);
make.centerX.equalTo(make.superview);
make.width.equalTo(#10.0f);
make.height.equalTo(#5.0f);
}];
I have also tried adding [myView setNeedsDisplay] in various places, but it didn't help.
Problem finally solved. I'm not sure what exactly caused the issue (Masonry [constraints framework] or iOS8 changes to UILabel), but the solution was to change the view hierarchy. I created another UIView that contains both UILabel and my UIView (drawn beak) instead of adding the UIView to UILabel as subview. Now drawRect method is called both on iOS7 and iOS8.
Previous hierarchy:
UIViewController View->Custom UILabel->MyView
New hierarchy:
UIViewController View->Container UIView->Custom UILabel & MyView
To sum up, if your drawRect method is not called on iOS8, and you are adding some UIView to UILabel as subview, try to use some container which will contain both UIView and UILabel.
make sure that myView's superview property clipToBounds = NO, and that myView rect is on screen
There is an important detail missing in your approach. You need to specify a frame that determines the origin and size of your view. This can be done with the initWithRect method of UIView, or you can set the frame after allocation/initialization. Since you have a custom init method already I would do the following:
myView = [MyView new];
myView.frame = CGRectMake(0.0,0.0,100.0,100.0);
[self addSubview:myView];
This will draw your custom view with an origin point of (0.0,0.0) and a width and height of 100.0.
Furthermore, adding a call to setNeedsDisplay should force the drawRect method to be called if you are still having trouble:
[myView setNeedsDisplay];
Related
I have a PopupView that extends UIView. In PopupView I have a UITextView.
When the PopupView show, my UITextView doesn't start at first line (it scroll a little bit to bottom)
So I use the code below to scroll the UITextView to top after PopupView appears
- (void)layoutSubviews{
[super layoutSubviews];
[self.contentTextView setContentOffset:CGPointZero animated:NO];
}
It works well in iOS9 (both device and simulator) but it doesn't work in iOS8
Any idea to fix it.
Any help would be great appreciated
UPDATE
I found that drawRect get called after layoutSubviews and if I setContentOffset:CGPointZero inside it, it will work
-(void)drawRect:(CGRect)rect{
[self.contentTextView setContentOffset:CGPointZero];
}
But I found the purpose of drawRect:
drawRect: - Implement this method if your view draws custom content.
If your view does not do any custom drawing, avoid overriding this
method.
Is it good to use drawRect without layoutSubviews in my case?
According to #longpham instruction, the drawRect() will use GPU so it's not good. Here is the solution that solve my problem
-(void)awakeFromNib{
[super awakeFromNib];
[self layoutIfNeeded]; // call layoutIfNeeded here to make layoutSubviews get called whenever layout change
}
- (void)layoutSubviews{
...
[self.contentTextView setContentOffset:CGPointZero];
}
it worked for me
- (void)drawRect:(CGRect)rect {
self.textView=[[UITextView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
[self addSubview:self.textView];
}
I have a UIScrollView, created programmatically, inside of a UIView. What do I need to do to ensure that I can use the delegate method scrollViewDidEndDecelerating?
Here's what I have set up, please assume that within the UIScrollView, that there are three UIImageViews. When the page first loads, I am looking at the center UIImageView and I can scroll once backwards or once forwards. The reason why I need this delegate method is because I intend to use it to calculate which UIImageView I am currently looking at.
ViewController.h
#interface ViewController : UIViewController <UIScrollViewDelegate>
ViewController.m
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView.pagingEnabled = YES;
[self.view addSubview: scrollView];
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
NSLog(#"scrollViewDidEndDecelerating");
}
Add
scrollView.delegate = self
after the scroll view initialization.
In your delegate method, you can test each uiimageview center to see Which one match the uiview center, That's the uiimageview you are looking at.
I want my UIView subclass to position itself automatically when it is added to a parent container view.
Can I somehow detect when it is added and run my positioning code then or do I need to do something like?
[parentView addSubview:subView];
[setView calcPosition];
UIView provides the methods willMoveToSuperview: and didMoveToSuperview. Just override those to know when the view is added to another view (or later removed).
Write calcPosition methord inside the subview and call it from the didMoveToSuperview of subview
- (void)didMoveToSuperview
{
[super didMoveToSuperview];
[self calcPosition];
}
CustomView *customView = [...];
[self.view addSubview:customView];
I need to detect in my CustomView class when it is added in other views or when my superview changes.
You can use willMoveToSuperview: and didMoveToSuperview to detect when the view is moved around. layoutSubviews will be called when the superview changes frame.
For a UIView use - (void)didMoveToSuperview
For a UIViewController use -(void)viewWillAppear:(BOOL)animated
also assign THE TAG of Customview before addsubview and the detect by Particular TAG.
Update: cause of the issue was found, see below.
I'm feeling a little embarrassed here since the answer to my question is probably dead simple, but I've been struggling with this piece of code for over a day and I can't figure out what's going on, so here goes:
I've created a custom UIView subclass. By now, I've isolated the code to the part that's causing me headaches:
-(id) initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
self.backgroundColor = [UIColor greenColor];
self.frame = CGRectMake(0, 0, 320, 60);
NSLog(#"%#", NSStringFromCGRect(self.frame));
}
return self;
}
This code is called when my custom view is loaded from a XIB file. This code attempts to resize the view. It does that, and the NSLog statement gives the same CGRect back as I pass to the view. However, whatever value I provide, the rectangle's height is consistently 50 points smaller than it's supposed to be.
If I create the view programmatically with similar code, the rectangle is drawn correctly:
-(id) initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor orangeColor];
self.frame = CGRectMake(0, 0, 320, 60);
NSLog(#"%#", NSStringFromCGRect(self.frame));
}
return self;
}
It drives me nuts. What could cause this?
As an additional question, what methods could I use to analyze / visualize the dimensions of views once the app is running in the iPhone simulator?
// Update:
I finally found what caused the weird behavior of this UIView subclass. The view controller that loaded this view as its subview was loaded from a Tab Bar, which has a height of 49 points, so that got me thinking. Apparently, the default autoresizing options that are selected in interface builder cause the view to resize, after the view's bounds get reset in my custom initializer method. Setting no autoresize behavior in Interface Builder solves the issue.
I finally found what caused the weird behavior of this UIView subclass. The view controller that loaded this view as its subview was loaded from a Tab Bar, which has a height of 49 points, so that got me thinking. Apparently, the default autoresizing options that are selected in interface builder cause the view to resize, after the view's bounds get reset in my custom initializer method. Setting no autoresize behavior in Interface Builder solves the issue.