I had the same need and my solution was pretty much what @Edmund said above.
The only downside I found so far is that since my query looks for the MAX() value of the sequence column, in theory, the number could be re-used if you delete the latest row (= by doing that you decrement the max recorded sequence number) and add a new one.
In some future iteration I will probably create a table dedicated to storing max values of each separate sequence, but I want to do some performance testing on how would that work under a heavy load. I'm effectively replacing native sequences without all their optimisations, which must come with downsides.
In my case, I am using the sequence numbers over a lot of columns with different partition columns, so I created a universal function I call from my triggers.
Here is the function. You'll need hstore extension to run it.
CREATE OR REPLACE FUNCTION nssequence_nextval()
RETURNS trigger AS $$
DECLARE
    query text;
    nextval bigint;
BEGIN
    IF TG_NARGS != 2 THEN
        RAISE EXCEPTION '% did not supply nssequence_nextval with the required arguments.', TG_NAME;
    END IF;
    query := 'SELECT COALESCE(MAX(%I)::text, ''0'')::bigint + 1 FROM %I.%I WHERE organisation_id = $1.organisation_id AND %I = $1.%I'::text; -- In my context, every row has an organisation_id as my app is multi-tenant-capable. Adjust this query to your needs.
    EXECUTE format(
        query,
        TG_ARGV[0], -- MAX(%I)::text, the column holding the sequence
        TG_TABLE_SCHEMA, TG_TABLE_NAME, -- FROM %I.%I - the table we are working with
        TG_ARGV[1], TG_ARGV[1] -- %I = $1.%I -- the column with the sequence namespace. Ideally a parent entity's UUID.
    ) USING NEW INTO nextval;
    -- The property name is the first argument of the trigger function, so we need hstore to set it.
    -- See https://stackoverflow.com/questions/7711432/how-to-set-value-of-composite-variable-field-using-dynamic-sql/7782641#7782641
    NEW := NEW #= hstore(TG_ARGV[0]::text, nextval::text);
    RETURN NEW;
END
$$ LANGUAGE 'plpgsql';
and here is the trigger for each column (replace things in square brackets with actual names):
CREATE OR REPLACE TRIGGER [nssequence_trigger_name]
BEFORE INSERT ON [schema.table]
FOR EACH ROW
EXECUTE PROCEDURE nssequence_nextval('[sequence_column_name]', '[partition_column_name]')
Just by the way, I have automated this. I use a column comment on each sequence column, such as nssequence:partition_column_name. I also wrote a script that looks for all columns that have the such comment and adds the triggers to them.
This is the query to find all sequence columns:
SELECT
    DISTINCT pgd.objoid, -- only used for filtering out duplicates
    c.table_schema,
    c.table_name,
    c.column_name,
    pgd.description
FROM pg_catalog.pg_statio_all_tables as st
INNER JOIN pg_catalog.pg_description pgd ON (
    pgd.objoid = st.relid
)
INNER JOIN information_schema.columns c ON (
    pgd.objsubid   = c.ordinal_position
    AND c.table_schema = st.schemaname
    AND c.table_name   = st.relname
)
INNER JOIN information_schema.tables t ON (
    t.table_type = 'BASE TABLE'
)
WHERE
    t.table_schema != 'information_schema'
    AND t.table_schema NOT LIKE 'pg_%'
    AND pgd.description LIKE 'nssequence%'
Finally I also have a cleanup mechanism in place that looks for triggers that are created over columns that no longer exist or no longer say they are a sequence number, but that's probably too much detail for this answer.