更改设备方向时重绘自定义 uiview

Posted

技术标签:

【中文标题】更改设备方向时重绘自定义 uiview【英文标题】:redraw a custom uiview when changing a device orientation 【发布时间】:2014-07-10 18:31:57 【问题描述】:
    如果我在- (void)drawRect:(CGRect)rect 内绘制图表就足以设置[_chartView setContentMode:UIViewContentModeRedraw],并且当设备改变它的方向时将调用此方法,并且可以计算 f.e.我的图表的新中心点。 如果我使用- (id)initWithFrame:(CGRect)frame 创建一个类似于子视图的视图,然后将其添加到视图控制器中,例如[self.view addSubview:chartView];。在这种情况下,我如何处理旋转以重绘图表?

【问题讨论】:

@Keenle,@Kijug 使用第二种方式绘制时不使用 drawRect 方法,请参阅 f.e github.com/kevinzhow/PNChart/blob/master/PNChart/PNPieChart/… 我已经更新了答案。必须创建一个带有图表控件的测试项目来解决问题。顺便说一句,图表组件既简单又很棒! 【参考方案1】:

虽然首选解决方案需要zero lines of code,但如果您必须触发重绘,请在setNeedsDisplay 中执行此操作,这反过来又会调用drawRect。 无需监听通知,也无需重构代码。

斯威夫特

override func layoutSubviews() 
    super.layoutSubviews()
    self.setNeedsDisplay()

Objective-C

- (void)layoutSubviews 
    [super layoutSubviews];
    [self setNeedsDisplay];


注意:layoutSubviewsUIView 方法,不是 UIViewController 方法。

【讨论】:

这似乎是一个很好的解决方案,但是当我将它放入我的 TableViewController 时,它不会在旋转时被调用。也许这是我的目标 ios (8.1) 或其他版本,但它不起作用。 layoutSubviewsUIView 方法,而不是 UIViewController 方法。您需要创建UITableView 的子类,而不是UITableViewController,这可以在IB 中完成。您可能想在 ***.com/a/5330162/218152 上阅读有关 layoutSubviews 的更多信息。 酷,谢谢。我的视图实际上已经通过在 IB 的 TableView 上设置 contentMode = .Redraw 进行了重绘。我遇到的问题是我自己的错,因为得到了纠正。谢谢! 这在我的测试中不起作用。方向更改不会触发重绘。我在我创建的子类 UIView 的类上使用它【参考方案2】:

为了让您的图表在设备方向改变时正确呈现,您需要更新图表的布局,下面是您应该添加到视图控制器的代码:

- (void)viewDidLayoutSubviews 
    [super viewDidLayoutSubviews];

    _chartView.frame = self.view.bounds;
    [_chartView strokeChart];

【讨论】:

我相信自从自动布局问世以来,它非常不鼓励在代码中设置框架,请检查我的答案【参考方案3】:

零代码行

使用.redraw

在创建自定义视图时以编程方式调用myView.contentMode = .redraw 就足够了。它是 IB 中的单个标志,因此是 0 行代码 的首选方式。请参阅堆栈溢出How to trigger drawRect on UIView subclass。

【讨论】:

【参考方案4】:

转到here 了解如何在设备方向更改时接收通知。当方向发生变化时,只需调用 [chartView setNeedsDisplay]; 以调用 drawRect: 以便您可以更新视图。希望这会有所帮助!

【讨论】:

这是正确的答案,UIView 子类应该自己处理。【参考方案5】:

您将添加到视图控制器的代码:

- (void)updateViewConstraints

    [super updateViewConstraints];
    [_chartView setNeedsDisplay];

【讨论】:

我试过了,但它没有在设备旋转时调用。 @DamienDelRusso 可能有其他原因导致它在设备旋转时没有被调用,环顾四周 我会看,但我发现我的视图正在重绘,contentMode 设置为 .Redraw。它对我不起作用的原因是我自己的愚蠢错误,因为已修复。谢谢!【参考方案6】:

不幸的是,一些答案建议覆盖控制器方法,但我有一些自定义 UITableViewCells 周围有阴影,旋转设备会拉伸单元格但不会重绘阴影。所以我不想将我的代码放在控制器中来(重新)绘制子视图。 我的解决方案是在我的自定义UITableViewCell 中收听UIDeviceOrientationDidChange 通知,然后按照建议调用setNeedsDisplay()

我的一个自定义 UITableViewCells 中的 Swift 4.0 代码示例

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()

顺便说一句:LoggertypealiasSwiftyBeaver。 感谢@Aderis 为我指明了正确的方向。

【讨论】:

【参考方案7】:

感谢 Keenle,提供上述 ObjC 解决方案。我整个早上都在忙于创建程序约束,扼杀了我的大脑,不明白为什么他们不会重新编译/重新对齐视图。我猜是因为我使用 CGRect 以编程方式创建了 UIView...这是优先的。

但是,这是我在 swift 中的解决方案:

    override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()
    myUIView.center = CGPoint(x: (UIScreen.mainScreen().bounds.width / 2), y: (UIScreen.mainScreen().bounds.height / 2))


松了一口气! :)

【讨论】:

【参考方案8】:

我用十几种方法尝试了“setNeedsDisplay”,但它无法将我正在制作动画的视图的阴影调整到新位置。

不过,我确实解决了我的问题。如果您的影子似乎不想合作/更新,您可以尝试将影子设置为零,然后再次设置:

    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
    )

如果您甚至还没有阴影并且需要该代码,那就是:

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

【讨论】:

以上是关于更改设备方向时重绘自定义 uiview的主要内容,如果未能解决你的问题,请参考以下文章

在自定义属性更改时重绘自定义 CALayer 子类

将 CALayer 添加到 UIView 并能够在方向更改时重绘

iPhone:如何在捏缩放uiscrollview时重绘子视图

自定义 UIView 重绘视图控制器

使用自动布局将自定义 UIView 绘制为背景并将其居中

自定义 UIViewController 未收到有关设备旋转的方向更改通知