Dynamically change the "access denied page" title and body
Access functions are only for calculating access results. You can't do anything else because this function is called multiple times, not only for incoming routes but also for outbound links.
To set a response use on403() of an ExceptionHtmlSubscriber. You can also set the result of a sub-request if you want a themed 403 message. See How can we catch the acces denied exception?
Edit: Answering further questions

1-Unfortunately my custom on403() method is never called (on the other
hand, the on403() method DefaultExceptionHtmlSubscriber is called):
What I am missing?

You need to register the service in mymodule.services.yml:
services:
  mymodule.exception_subscriber:
    class: Drupal\mymodule\EventSubscriber\MymoduleExceptionHtmlSubscriber
    tags:
      - { name: event_subscriber }


2-Let say that my custom on403() method is called: what should be the
code to redirect the user on my custom 403 page containing my custom
message (ie: it could be nice to display the reason of the
AccessResult AccessResult::forbidden(&quot;a good reason&quot;);

You find the message in the exception object:
public function on403(ExceptionEvent $event) {
  // ...
  if ($my_condition) {
    $message = $event-&gt;getThrowable()-&gt;getMessage();
    $event-&gt;setResponse(new Response($message, 403));
  }
}

BTW you don't need to call parent::on403($event) because the parent registered with a lower priority will be called automatically when your event subscriber doesn't set a response.