I did some digging in the source and found that there is actually events being fired. But there is a lot of noise there that makes it less than ideal for an API.
Sketchup::PagesObserver#onContentsModified is triggered when:
kPageMovedkSelectedPageChangedkNameChangedkRemoveAllkPageActivate
“changed” vs “active” you ask? Good question - that’s some internal implementation noise leaking through.
When the active page is changed you get two calls to onContentsModified. The first one is before the Sketchup::Pages#selected_page getter will reflect the new page. The second once it’s all set.
For instance, given three scenes, toggle from Scene 1 to Scene 2 you get this result:
class MyPagesObserver < Sketchup::PagesObserver
def onContentsModified(pages)
puts "onContentsModified: #{pages.selected_page.name}"
end
end
# Attach the observer.
Sketchup.active_model.pages.add_observer(MyPagesObserver.new)

So there is an event - but you can’t easily tell exactly what changed. But there’ll be a lot less noise than using the view or framechange observers.