- If I draw my chart inside
- (void)drawRect:(CGRect)rect
is just enough to set [_chartView setContentMode:UIViewContentModeRedraw]
and this method will be called when device changes it's orienatation and it's possible to calculate f.e. new center point for my chart. - If I create a view like a subview using
- (id)initWithFrame:(CGRect)frame
and then add it in view controller like[self.view addSubview:chartView];
. How in this case I can handle rotation to redraw my chart?

- 5,667
- 12
- 59
- 97
-
@Keenle,@Kijug when you draw using the second way you do not use drawRect method, see f.e https://github.com/kevinzhow/PNChart/blob/master/PNChart/PNPieChart/PNPieChart.m – pvllnspk Jul 11 '14 at 07:56
-
+ needs to call redraw method only first time when device orientation changes like drawRect does – pvllnspk Jul 11 '14 at 08:02
-
I've updated the answer. Had to create a test project with chart control to figure out the problem. BTW the charts component is simple and great! – Keenle Jul 11 '14 at 13:24
8 Answers
While a preferred solution requires zero lines of code, if you must trigger a redraw, do so in setNeedsDisplay
, which in turn invokes drawRect
.
No need to listen to notifications nor refactor the code.
Swift
override func layoutSubviews() {
super.layoutSubviews()
self.setNeedsDisplay()
}
Objective-C
- (void)layoutSubviews {
[super layoutSubviews];
[self setNeedsDisplay];
}
Note:
layoutSubviews
is a UIView
method, not a UIViewController
method.

- 47,376
- 28
- 140
- 179
-
This seems like a good solution, but when I put this into my TableViewController it is not called upon rotation. Perhaps it's my target version of iOS (8.1) or something, but it doesn't work. – Damien Del Russo Dec 30 '15 at 02:54
-
1`layoutSubviews` is a `UIView` method, not a `UIViewController` method. You need to make a subclass of `UITableView`, not `UITableViewController`, which can be done in **IB**. You may want to read more about layoutSubviews on http://stackoverflow.com/a/5330162/218152. – SwiftArchitect Dec 30 '15 at 05:04
-
Cool, thanks. My views were actually redrawing already from setting contentMode = .Redraw on the TableView in IB. The problem I had was my own fault, since corrected. Thanks! – Damien Del Russo Dec 31 '15 at 18:10
-
This does not work in my testing. Orientation change does not trigger a redraw. And I am using this on a class I created that subclasses UIView – johnny Jul 26 '19 at 20:08
To make your chart rendered correctly when device orientation changes you need to update chart's layout, here is the code that you should add to your view controller:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_chartView.frame = self.view.bounds;
[_chartView strokeChart];
}

- 12,010
- 3
- 37
- 46
-
2I believe its highly discouraged to set frame in code since auto-layouts came out, check my answer – 2cupsOfTech Feb 23 '15 at 17:13
Zero Lines of Code
Use .redraw
Programmatically invoking myView.contentMode = .redraw
when creating the custom view should suffice. It is a single flag in IB and, as such, the 0 lines of code prefered way. See Stack Overflow How to trigger drawRect on UIView subclass.

- 1
- 1

- 47,376
- 28
- 140
- 179
Go here to learn how to receive notifications for when the device orientation changes. When the orientation does change, just call [chartView setNeedsDisplay];
to make drawRect:
get called so you can update your view. Hope this helps!

- 1
- 1

- 2,072
- 1
- 20
- 29
-
This is the right answer, the UIView subclass shall handle this by itself. – Eddie Deng Feb 02 '16 at 13:52
The code you'll add to your view controller:
- (void)updateViewConstraints
{
[super updateViewConstraints];
[_chartView setNeedsDisplay];
}

- 5,953
- 4
- 34
- 57
-
-
@DamienDelRusso there might be some other reason why its not being called for you on device rotation, look around – 2cupsOfTech Dec 30 '15 at 12:58
-
I would look, but I found out my views are redrawing with contentMode set to .Redraw. The reason it wasn't working for me was my own dumb error, since fixed. Thanks! – Damien Del Russo Dec 31 '15 at 18:08
Unfortunately some answers suggest to override controller methods, but I've some custom UITableViewCells
with a shadow around and rotating the device stretches the cells but doesn't redraw the shadow. So I don't want to put my code within a controller to (re)draw a subview.
The solution for me is to listen to a UIDeviceOrientationDidChange
notification within my custom UITableViewCell
and then call setNeedsDisplay()
as suggested.
Swift 4.0 Code example in one of my custom UITableViewCells
override func awakeFromNib() {
super.awakeFromNib()
NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChangeNotification), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
@objc func deviceOrientationDidChangeNotification(_ notification: Any) {
Logger.info("Orientation changed: \(notification)")
setNeedsLayout()
}
BTW: Logger
is a typealias
to SwiftyBeaver
.
Thanks to @Aderis pointing me to the right direction.

- 1,230
- 13
- 21
Thanks Keenle, for the above ObjC solution. I was messing around with creating programmed constraints all morning, killing my brain, not understanding why they would not recompile/realign the view. I guess because I had created the UIView programmatically using CGRect...this was taking precedence.
However, here was my solution in swift:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myUIView.center = CGPoint(x: (UIScreen.mainScreen().bounds.width / 2), y: (UIScreen.mainScreen().bounds.height / 2))
}
So relieved! :)

- 5,383
- 7
- 50
- 89

- 1,550
- 1
- 18
- 31
I tried "setNeedsDisplay" in a dozen ways and it didn't work for adjusting the shadow of a view I was animating to a new position.
I did solve my problem with this though. If your shadow doesn't seem to want to cooperate/update, you could try just setting the shadow to nil, then setting it again:
UIView.animateKeyframes(withDuration: 0.2 /*Total*/, delay: 0.0, options: UIViewKeyframeAnimationOptions(), animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 10/10, animations:{
// Other things to animate
//Set shadow to nil
self.viewWithShadow.layer.shadowPath = nil
})
}, completion: { finished in
if (!finished) { return }
// When the view is moved, set the shadow again
self.viewWithShadow.layer.shadowPath = UIBezierPath(rect: self.descTextView.bounds).cgPath
})
If you don't even have a shadow yet and need that code, here's that:
func addShadowTo (view: UIView) {
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.gray.cgColor
view.layer.shadowOffset = CGSize( width: 1.0, height: 1.0)
view.layer.shadowOpacity = 0.5
view.layer.shadowRadius = 6.0
view.layer.shadowPath = UIBezierPath(rect: view.bounds).cgPath
view.layer.shouldRasterize = true
}

- 12,042
- 7
- 57
- 83