pvllnspk
pvllnspk

Reputation: 5767

redraw a custom uiview when changing a device orientation

  1. 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.
  2. 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?

Upvotes: 29

Views: 32180

Answers (8)

David West
David West

Reputation: 1590

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! :)

Upvotes: 0

SwiftArchitect
SwiftArchitect

Reputation: 48524

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.

Upvotes: 42

SwiftArchitect
SwiftArchitect

Reputation: 48524

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.

Upvotes: 8

Dave
Dave

Reputation: 12216

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
}

Upvotes: 0

Sal
Sal

Reputation: 1238

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.

Upvotes: 1

2cupsOfTech
2cupsOfTech

Reputation: 6073

The code you'll add to your view controller:

- (void)updateViewConstraints
{
    [super updateViewConstraints];
    [_chartView setNeedsDisplay];
}

Upvotes: 2

Keenle
Keenle

Reputation: 12220

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];
}

Upvotes: 15

Adam Evans
Adam Evans

Reputation: 2082

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!

Upvotes: 4

Related Questions